1#!/usr/bin/ruby 2# encoding: utf-8 3 4require 'spec' 5require 'antlr3/template' 6require 'antlr3/util' 7 8include ANTLR3 9MethodDescription = Struct.new( :name, :body, :arguments ) 10TEMPLATE_NAMES = %w( method class_definition attribute ) 11SAMPLE_GROUP_FILE = File.join( 12 File.dirname( __FILE__ ), 'sample-input', 'template-group' 13) 14 15describe 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 70end 71 72 73shared_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 126end 127 128describe 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 206end 207 208describe 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 250end 251