Skip to content

Commit

Permalink
Add "constructor from fields" generator
Browse files Browse the repository at this point in the history
  • Loading branch information
andreyvit committed Nov 29, 2010
1 parent b100707 commit 409e827
Show file tree
Hide file tree
Showing 9 changed files with 380 additions and 16 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ Changes patched live into the sources:
}

* add missing field declarations for properties (unless those have getters implemented as methods)
* add an initializing constructor for fields marked with `!c`

Collected in `xdry.m`:

Expand Down
91 changes: 91 additions & 0 deletions lib/xdry/generators/ctor_from_field.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@

module XDry
module Generators

class ConstructorFromField < Generator
id "ctor-from-field"

def process_class oclass
attributes = oclass.attributes.select { |a| a.wants_constructor? }
return if attributes.empty?

patch_implementation! oclass, attributes
patch_interface! oclass, attributes
end

def patch_implementation! oclass, attributes
impl_selector_def = impl_selector_def_for(attributes)

init_code = Emitter.capture do |o|
o.method "(id)#{impl_selector_def}" do
o.if "self = [super init]" do
end
o << "return self;"
end
end

MethodPatcher.new(patcher, oclass, impl_selector_def.selector, ImplementationStartIP.new(oclass), init_code) do |omethod|
impl = omethod.impl
ip = InsideConstructorIfSuperIP.new(impl)

lines = Emitter.capture do |o|
attributes.zip(impl_selector_def.components).each do |oattr, selector_component|
name, type = oattr.name, oattr.type
capitalized_name = name.capitalized_identifier

retain_policy = Boxing.retain_policy_of type

unless impl.children.any? { |n| NLine === n && n.line =~ /^(?:self\s*.\s*#{oattr.name}|#{oattr.field_name})\s*=/ }

var_name = impl.start_node.selector_def.var_name_after_keyword(selector_component.keyword)

field_ref = oattr.field_name
field_ref = "self->#{field_ref}" if field_ref == var_name

retained = retain_policy.retain(var_name)
o << "#{field_ref} = #{retained};"
end
end
end

ip.insert @patcher, lines unless lines.empty?
end
end

def patch_interface! oclass, attributes
intf_selector_def = intf_selector_def_for(attributes)

omethod = oclass.find_method(intf_selector_def.selector)
unless omethod && omethod.has_header?

ip = BeforeInterfaceEndIP.new(oclass)
lines = Emitter.capture do |o|
o << "- (id)#{intf_selector_def};"
end
ip.insert patcher, [""] + lines + [""]

end
end

def impl_selector_def_for attributes
CompoundSelectorDef.new(attributes.each_with_index.collect { |a, i| selector_component_for(a, i, true) })
end

def intf_selector_def_for attributes
CompoundSelectorDef.new(attributes.each_with_index.collect { |a, i| selector_component_for(a, i, false) })
end

def selector_component_for attribute, index, is_impl
keyword = attribute.name
keyword = "initWith#{keyword.capitalized_identifier}" if index == 0

arg_name = attribute.name
arg_name = arg_name.prefixed_as_arg_name if is_impl && arg_name == attribute.field_name

SelectorComponent.new("#{keyword}:", arg_name, attribute.type)
end

end

end
end
10 changes: 2 additions & 8 deletions lib/xdry/generators/dictionary_coding.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,7 @@ def process_class oclass
name, type = oattr.name, oattr.type
field_name = oattr.field_name
raw_name = "#{name}Raw"
capitalized_name = case name
when 'id', 'uid' then name.upcase
else name[0..0].upcase + name[1..-1]
end
capitalized_name = name.capitalized_identifier
key_const = "#{capitalized_name}Key"

type_boxer = Boxing.converter_for type
Expand Down Expand Up @@ -112,10 +109,7 @@ def each_persistent_attr oclass
oclass.attributes.select { |a| a.persistent? }.each do |oattr|
name, type = oattr.name, oattr.type
field_name = oattr.field_name
capitalized_name = case name
when 'id', 'uid' then name.upcase
else name[0..0].upcase + name[1..-1]
end
capitalized_name = name.capitalized_identifier
key_const = "#{capitalized_name}Key"

type_boxer = Boxing.converter_for type
Expand Down
8 changes: 8 additions & 0 deletions lib/xdry/parsing/model.rb
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,10 @@ def wants_property?
has_field_def? && field_def.wants_property?
end

def wants_constructor?
has_field_def? && field_def.wants_constructor?
end

def getter_selector
# FIXME: need to account for a possible selector override declared in @property
@name
Expand Down Expand Up @@ -240,6 +244,10 @@ def add_method_impl! method_impl
@impl = method_impl.bind(self)
end

def has_header?
not @header.nil?
end

def has_impl?
not @impl.nil?
end
Expand Down
4 changes: 4 additions & 0 deletions lib/xdry/parsing/nodes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ def wants_property?
tagged_with? 'wants-property'
end

def wants_constructor?
tagged_with? 'wants-constructor'
end

def to_s
"#{@type} #{@name}#{tags_comment}"
end
Expand Down
16 changes: 9 additions & 7 deletions lib/xdry/parsing/parsers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,15 @@ def initialize scope

def parse_line! line, eol_comments, indent
@this_line_tags = Set.new
if line =~ /!p\b/
marker = $&
is_full_line = line.gsub(marker, '').strip.empty?
klass = is_full_line ? NFullLineMarker : NPartLineMarker
yield klass.new(marker)
(is_full_line ? @tags : @this_line_tags) << 'wants-property'
return if is_full_line
[[/!p\b/, 'wants-property'], [/!c\b/, 'wants-constructor']].each do |regexp, tag|
if line =~ regexp
marker = $&
is_full_line = line.gsub(marker, '').strip.empty?
klass = is_full_line ? NFullLineMarker : NPartLineMarker
yield klass.new(marker)
(is_full_line ? @tags : @this_line_tags) << tag
return if is_full_line
end
end
case line
when /\}/
Expand Down
2 changes: 1 addition & 1 deletion lib/xdry/patching/patcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def do_delete_lines file_ref, line_index, line_count
lines[line_index .. line_index+line_count-1].each { |line| puts " #{line}" }
end

file_ref.fixup_positions! line_index+1+line_count, -line_count
file_ref.fixup_positions! line_index+line_count, -line_count
lines[line_index .. line_index+line_count-1] = []
end

Expand Down
13 changes: 13 additions & 0 deletions lib/xdry/support/string_additions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,19 @@ module StringAdditions
def blank?
self =~ /^\s*$/
end

def capitalized_identifier
case self
when 'id', 'uid' then upcase
else self[0..0].upcase + self[1..-1]
end
end

def prefixed_as_arg_name
prefix = case self when /^[aeiou]/ then 'an' else 'a' end

prefix + self.capitalized_identifier
end

end

Expand Down
Loading

0 comments on commit 409e827

Please sign in to comment.