Home | History | Annotate | Download | only in unit
      1 #!/usr/bin/ruby
      2 # encoding: utf-8
      3 
      4 require 'spec'
      5 require 'antlr3/template'
      6 require 'antlr3/util'
      7 
      8 include ANTLR3
      9 MethodDescription = Struct.new( :name, :body, :arguments )
     10 TEMPLATE_NAMES = %w( method class_definition attribute )
     11 SAMPLE_GROUP_FILE = File.join(
     12   File.dirname( __FILE__ ), 'sample-input', 'template-group'
     13 )
     14 
     15 describe Template::Context do
     16   example "creating an empty context" do
     17     context = Template::Context.new
     18     context.instance_variables.should be_empty
     19   end
     20   
     21   example "creating a context with a variable map" do
     22     context = Template::Context.new(
     23       :a => 1, :b => 2
     24     )
     25     
     26     vars = context.instance_variables.map { | i | i.to_s }
     27     vars.should include( '@a' )
     28     vars.should include( '@b' )
     29     
     30     context.instance_variable_get( '@a' ).should == 1
     31     context.instance_variable_get( '@b' ).should == 2
     32   end
     33   
     34   example "fetching variable values from []" do
     35     context = Template::Context.new(
     36       :a => 1, :b => 2
     37     )
     38     
     39     context[ :a ].should == 1
     40     context[ 'a' ].should == 1
     41     context[ :b ].should == 2
     42     context[ 'b' ].should == 2
     43   end
     44   
     45   example "defining variables with []=" do
     46     context = Template::Context.new( :a => 3 )
     47     context[ :a ] = 1
     48     context[ 'b' ] = 2
     49     
     50     context.instance_variable_get( '@a' ).should == 1
     51     context.instance_variable_get( '@b' ).should == 2
     52   end
     53   
     54   example "using method missing to assign values" do
     55     context = Template::Context.new( :a => 3 )
     56     context.a = 1
     57     context.b = 2
     58     
     59     context.instance_variable_get( '@a' ).should == 1
     60     context.instance_variable_get( '@b' ).should == 2
     61   end
     62   
     63   example "using method missing to get variable values" do
     64     context = Template::Context.new( :a => 1, :b => 2)
     65     
     66     context.a.should == 1
     67     context.b.should == 2
     68   end
     69   
     70 end
     71 
     72 
     73 shared_examples_for "template groups" do
     74   include ANTLR3::Util
     75   
     76   example "template definitions" do
     77     templates = @group.templates
     78     templates.should_not be_empty
     79     templates.should equal @group::TEMPLATES
     80     
     81     names = templates.keys
     82     
     83     names.should have(3).things
     84     for template_name in TEMPLATE_NAMES
     85       names.should include template_name
     86       template_class = templates[ template_name ]
     87       template_class.should be_a_kind_of Class
     88       template_class.superclass.should equal Template::Context
     89       template_class.should be < @group # it should include the group module
     90     end
     91   end
     92   
     93   example "template_defined?( name ) should verify whether a template is defined in a group" do
     94     for name in TEMPLATE_NAMES
     95       @group.template_defined?( name ).should be_true
     96       @group.template_defined?( name.to_s ).should be_true
     97     end
     98     
     99     @group.template_defined?( :something_else ).should be_false
    100   end
    101   
    102   example "template method definitions" do
    103     for name in TEMPLATE_NAMES
    104       @group.should respond_to( name )
    105       @group.should respond_to( "#{ name }!" )
    106       if RUBY_VERSION =~ /^1\.9/
    107         @group.private_instance_methods.should include name.to_sym
    108         @group.private_instance_methods.should include :"#{ name }!"
    109       else
    110         @group.private_instance_methods.should include name.to_s
    111         @group.private_instance_methods.should include "#{ name }!"
    112       end
    113     end
    114   end
    115   
    116   example "template method operation" do
    117     value = @group.class_definition
    118     value.should be_a_kind_of Template::Context
    119     
    120     value = @group.class_definition!
    121     value.should be_a_kind_of String
    122     
    123     value = @group.attribute( :name => 'a' )
    124     value.should be_a_kind_of Template::Context
    125   end
    126 end
    127 
    128 describe Template::Group, "dynamic template definition" do
    129   include ANTLR3::Util
    130   
    131   before :each do
    132     @group = Template::Group.new do
    133       extend ANTLR3::Util
    134       define_template( :class_definition, tidy( <<-'END'.chomp ) )
    135       | class <%= @name %><% if @superclass %> < <%= @superclass %><% end %>
    136       | % if @attributes
    137       | 
    138       | % for attr, access in @attributes 
    139       | <%= attribute( :name => attr, :access => ( access || 'rw' ) ).to_s.chomp %>
    140       | % end
    141       | % end
    142       | % if @methods
    143       | % for method in ( @methods || [] )
    144       | <%= method( method ) %>
    145       | % end
    146       | % end
    147       | end
    148       END
    149       
    150       define_template( :attribute, tidy( <<-'END'.chomp ) )
    151       | % case @access.to_s.downcase
    152       | % when 'r'
    153       |   attr_reader :<%= @name %>
    154       | % when 'w'
    155       |   attr_writer :<%= @name %>
    156       | % else
    157       |   attr_accessor :<%= @name %>
    158       | % end
    159       END
    160       
    161       define_template( :method, tidy( <<-'END'.chomp ) )
    162       |   
    163       |   def <%= @name %><% if @arguments and not @arguments.empty? %>( <%= @arguments.join( ', ' ) %> )<% end %>
    164       | <%= @body.gsub( /^/, '    ' ) %>
    165       |   end
    166       END
    167     end
    168   end
    169   
    170   it_should_behave_like "template groups"
    171   
    172   example "template object string rendering" do
    173     attributes = [
    174       %w( family ),
    175       %w( name r )
    176     ]
    177           
    178     methods = [
    179       MethodDescription.new( 'eat', %q[puts( "ate %s %s" % [ number, @name ] )], %w( number ) ),
    180       MethodDescription.new( :to_s, '@name.to_s.dup' )
    181     ]
    182     
    183     vegetable = @group.class_definition(
    184       :name => 'Vegetable',
    185       :superclass => 'Food',
    186       :attributes => attributes,
    187       :methods => methods
    188     )
    189     
    190     vegetable.to_s.should == tidy( <<-END.chomp )
    191     | class Vegetable < Food
    192     |
    193     |   attr_accessor :family
    194     |   attr_reader :name
    195     |   
    196     |   def eat( number )
    197     |     puts( "ate %s %s" % [ number, @name ] )
    198     |   end
    199     |   
    200     |   def to_s
    201     |     @name.to_s.dup
    202     |   end
    203     | end
    204     END
    205   end
    206 end
    207 
    208 describe Template::Group, "loading a template definition file" do
    209   
    210   before :each do
    211     @group = Template::Group.load( SAMPLE_GROUP_FILE )
    212   end
    213   
    214   it_should_behave_like "template groups"
    215   
    216   example "template object string rendering" do
    217     attributes = [
    218       %w( family ),
    219       %w( name r )
    220     ]
    221           
    222     methods = [
    223       MethodDescription.new( 'eat', %q[puts( "ate %s %s" % [ number, @name ] )], %w( number ) ),
    224       MethodDescription.new( :to_s, '@name.to_s.dup' )
    225     ]
    226     
    227     vegetable = @group.class_definition(
    228       :name => 'Vegetable',
    229       :superclass => 'Food',
    230       :attributes => attributes,
    231       :methods => methods
    232     )
    233     
    234     vegetable.to_s.should == tidy( <<-END.chomp )
    235     | class Vegetable < Food
    236     |
    237     |   attr_accessor :family
    238     |   attr_reader :name
    239     |   
    240     |   def eat( number )
    241     |     puts( "ate %s %s" % [ number, @name ] )
    242     |   end
    243     |   
    244     |   def to_s
    245     |     @name.to_s.dup
    246     |   end
    247     | end
    248     END
    249   end
    250 end
    251