inversion 0.6.1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
data.tar.gz.sig CHANGED
Binary file
data/History.rdoc CHANGED
@@ -1,3 +1,9 @@
1
+ == v0.7.0 [2012-03-29] Michael Granger <ged@FaerieMUD.org>
2
+
3
+ - Switch to a much more flexible way to render tag bodies. This should
4
+ resolve most of the problems we've encountered with complex templates.
5
+
6
+
1
7
  == v0.6.1 [2012-03-16] Michael Granger <ged@FaerieMUD.org>
2
8
 
3
9
  - Commented out some of the more expensive debug logging for an order
data/Rakefile CHANGED
@@ -35,6 +35,7 @@ hoespec = Hoe.spec 'inversion' do
35
35
  self.dependency 'sysexits', '~> 1.0', :development
36
36
 
37
37
  self.spec_extras[:licenses] = ["BSD"]
38
+ self.spec_extras[:rdoc_options] = ['-f', 'fivefish', '-t', 'Inversion Templates']
38
39
  self.require_ruby_version( '>=1.9.2' )
39
40
  self.hg_sign_tags = true if self.respond_to?( :hg_sign_tags= )
40
41
  self.check_history_on_release = true if self.respond_to?( :check_history_on_release= )
data/lib/inversion.rb CHANGED
@@ -26,10 +26,10 @@ module Inversion
26
26
  require 'inversion/monkeypatches'
27
27
 
28
28
  # Library version constant
29
- VERSION = '0.6.1'
29
+ VERSION = '0.7.0'
30
30
 
31
31
  # Version-control revision constant
32
- REVISION = %q$Revision: 1947a577fae1 $
32
+ REVISION = %q$Revision: 92c2961cf986 $
33
33
 
34
34
  #
35
35
  # Logging
@@ -8,6 +8,63 @@ require 'inversion' unless defined?( Inversion )
8
8
  class Inversion::RenderState
9
9
  include Inversion::Loggable
10
10
 
11
+
12
+ # An encapsulation of the scope in which the bodies of tags evaluate. It's
13
+ # used to provide a controlled, isolated namespace which remains the same from
14
+ # tag to tag.
15
+ class Scope < BasicObject
16
+
17
+ ### Create a new RenderState::Scope with its initial tag locals set to
18
+ ### +locals+.
19
+ def initialize( locals={} )
20
+ @locals = locals
21
+ end
22
+
23
+
24
+ ### Return the tag local with the specified +name+.
25
+ def []( name )
26
+ return @locals[ name.to_sym ]
27
+ end
28
+
29
+
30
+ ### Set the tag local with the specified +name+ to +value+.
31
+ def []=( name, value )
32
+ @locals[ name.to_sym ] = value
33
+ end
34
+
35
+
36
+ ### Return a copy of the receiving Scope merged with the given +values+,
37
+ ### which can be either another Scope or a Hash.
38
+ def +( values )
39
+ # Have to do it this kludgy way because Scopes don't have #respond_to?
40
+ begin
41
+ return Scope.new( @locals.merge(values.__locals__) )
42
+ rescue ::NameError
43
+ return Scope.new( @locals.merge(values) )
44
+ end
45
+ end
46
+
47
+
48
+ ### Return the Hash of tag locals the belongs to this scope.
49
+ def __locals__
50
+ return @locals
51
+ end
52
+
53
+
54
+ #########
55
+ protected
56
+ #########
57
+
58
+ ### The main trickery behind this class -- intercept tag locals as method calls
59
+ ### and map them into values from the Scope's locals.
60
+ def method_missing( sym, *args, &block )
61
+ return super unless sym =~ /^\w+$/
62
+ @locals[ sym ]
63
+ end
64
+
65
+ end # class Scope
66
+
67
+
11
68
  ### Create a new RenderState. If the template is being rendered inside another one, the
12
69
  ### containing template's RenderState will be passed as the +containerstate+. The
13
70
  ### +initial_attributes+ will be deep-copied, and the +options+ will be merged with
@@ -25,10 +82,12 @@ class Inversion::RenderState
25
82
  # self.log.debug "Creating a render state with attributes: %p" %
26
83
  # [ initial_attributes ]
27
84
 
85
+ locals = deep_copy( initial_attributes )
86
+ @scopes = [ Scope.new(locals) ]
87
+
28
88
  @start_time = Time.now
29
89
  @containerstate = containerstate
30
90
  @options = Inversion::Template::DEFAULT_CONFIG.merge( options )
31
- @attributes = [ deep_copy(initial_attributes) ]
32
91
  @block = block
33
92
  @default_errhandler = self.method( :default_error_handler )
34
93
  @errhandler = @default_errhandler
@@ -77,8 +136,8 @@ class Inversion::RenderState
77
136
 
78
137
  ### Return the hash of attributes that are currently in effect in the
79
138
  ### rendering state.
80
- def attributes
81
- return @attributes.last
139
+ def scope
140
+ return @scopes.last
82
141
  end
83
142
 
84
143
 
@@ -92,7 +151,13 @@ class Inversion::RenderState
92
151
  ### return the result.
93
152
  def eval( code )
94
153
  self.log.debug "Evaling: %p" [ code ]
95
- return self.instance_eval( code )
154
+ return self.scope.instance_eval( code )
155
+ end
156
+
157
+
158
+ ### Backward-compatibility -- return the tag locals of the current scope as a Hash.
159
+ def attributes
160
+ return self.scope.__locals__
96
161
  end
97
162
 
98
163
 
@@ -103,10 +168,11 @@ class Inversion::RenderState
103
168
  self.log.debug "Overriding template attributes with: %p" % [ overrides ]
104
169
 
105
170
  begin
106
- @attributes.push( @attributes.last.merge(overrides) )
171
+ newscope = self.scope + overrides
172
+ @scopes.push( newscope )
107
173
  yield( self )
108
174
  ensure
109
- @attributes.pop
175
+ @scopes.pop
110
176
  end
111
177
  end
112
178
 
@@ -178,7 +244,8 @@ class Inversion::RenderState
178
244
  ### Merge the attributes and options of the +otherstate+ with those of the receiver,
179
245
  ### replacing any with the same keys.
180
246
  def merge!( otherstate )
181
- self.attributes.merge!( otherstate.attributes )
247
+ @scopes.push( @scopes.pop + otherstate.scope )
248
+ # self.attributes.merge!( otherstate.attributes )
182
249
  self.options.merge!( otherstate.options )
183
250
  return self
184
251
  end
@@ -331,11 +398,11 @@ class Inversion::RenderState
331
398
 
332
399
  ### Return a human-readable representation of the object.
333
400
  def inspect
334
- return "#<%p:0x%08x containerstate: %s, attributes: %s, destination: %p>" % [
401
+ return "#<%p:0x%08x containerstate: %s, scope locals: %s, destination: %p>" % [
335
402
  self.class,
336
403
  self.object_id / 2,
337
404
  self.containerstate ? "0x%08x" % [ self.containerstate.object_id ] : "nil",
338
- self.attributes.keys.sort.join(', '),
405
+ self.scope.__locals__.keys.sort.join(', '),
339
406
  self.destination.class,
340
407
  ]
341
408
  end
@@ -364,9 +431,9 @@ class Inversion::RenderState
364
431
 
365
432
  ### Handle attribute methods.
366
433
  def method_missing( sym, *args, &block )
367
- return super unless sym.to_s =~ /^[a-z]\w+[\?=!]?$/
368
- self.log.debug "mapping missing method call to attribute: %p" % [ sym ]
369
- return self.attributes[ sym ]
434
+ return super unless sym.to_s =~ /^\w+$/
435
+ self.log.debug "mapping missing method call to tag local: %p" % [ sym ]
436
+ return self.scope[ sym ]
370
437
  end
371
438
 
372
439
 
@@ -79,10 +79,7 @@ class Inversion::Template::AttrTag < Inversion::Template::CodeTag
79
79
 
80
80
  ### Render the tag attributes of the specified +renderstate+ and return them.
81
81
  def render( renderstate )
82
- # self.log.debug "Rendering %p with state: %p" % [ self, renderstate ]
83
-
84
- # Evaluate the tag body and return either false value
85
- value = self.evaluate( renderstate ) or return value
82
+ value = self.evaluate( renderstate ) # :FIXME: or return value # nil or false?
86
83
 
87
84
  # Apply the format if there is one
88
85
  if self.format
@@ -95,21 +92,8 @@ class Inversion::Template::AttrTag < Inversion::Template::CodeTag
95
92
 
96
93
  ### Evaluate the body of the tag in the context of +renderstate+ and return the results.
97
94
  def evaluate( renderstate )
98
- value = nil
99
- attribute = renderstate.attributes[ self.name.to_sym ]
100
- # self.log.debug " initial attribute: %p" % [ attribute ]
101
-
102
- # Evaluate the method chain (if there is one) against the attribute
103
- if self.methodchain
104
- methodchain = "self" + self.methodchain
105
- # self.log.debug " evaling methodchain: %p on: %p" % [ methodchain, attribute ]
106
- value = attribute.instance_eval( methodchain )
107
- else
108
- value = attribute
109
- end
110
- # self.log.debug " evaluated value: %p" % [ value ]
111
-
112
- return value
95
+ code = [ self.name.to_s, self.methodchain ].join( '' )
96
+ return renderstate.eval( code )
113
97
  end
114
98
 
115
99
 
@@ -48,11 +48,11 @@ describe Inversion::RenderState do
48
48
 
49
49
  state = Inversion::RenderState.new( attributes )
50
50
 
51
- state.attributes.should_not equal( attributes )
52
- state.attributes[:foot].should == "in mouth"
53
- state.attributes[:foot].should_not equal( attributes[:foot] )
54
- state.attributes[:bear].should == "in woods"
55
- state.attributes[:bear].should_not equal( attributes[:bear] )
51
+ state.scope.__locals__.should_not equal( attributes )
52
+ state.scope[:foot].should == "in mouth"
53
+ state.scope[:foot].should_not equal( attributes[:foot] )
54
+ state.scope[:bear].should == "in woods"
55
+ state.scope[:bear].should_not equal( attributes[:bear] )
56
56
  end
57
57
 
58
58
  it "preserves tainted status when copying its attributes" do
@@ -61,7 +61,7 @@ describe Inversion::RenderState do
61
61
 
62
62
  state = Inversion::RenderState.new( attributes )
63
63
 
64
- state.attributes[:danger].should be_tainted()
64
+ state.scope[:danger].should be_tainted()
65
65
  end
66
66
 
67
67
  it "preserves singleton methods on attribute objects when copying" do
@@ -70,7 +70,7 @@ describe Inversion::RenderState do
70
70
 
71
71
  state = Inversion::RenderState.new( :foo => obj )
72
72
 
73
- state.attributes[:foo].singleton_methods.map( &:to_sym ).should include( :foo )
73
+ state.scope[:foo].singleton_methods.map( &:to_sym ).should include( :foo )
74
74
  end
75
75
 
76
76
  it "preserves frozen status when copying its attributes" do
@@ -79,7 +79,7 @@ describe Inversion::RenderState do
79
79
 
80
80
  state = Inversion::RenderState.new( attributes )
81
81
 
82
- state.attributes[:danger].should be_frozen()
82
+ state.scope[:danger].should be_frozen()
83
83
  end
84
84
 
85
85
  it "can override its attributes for the duration of a block" do
@@ -92,7 +92,7 @@ describe Inversion::RenderState do
92
92
  state.bear.should == 'in woods'
93
93
  end
94
94
 
95
- state.attributes[:foot].should == 'in mouth'
95
+ state.scope[:foot].should == 'in mouth'
96
96
  end
97
97
 
98
98
 
@@ -107,7 +107,7 @@ describe Inversion::RenderState do
107
107
  end
108
108
  }.to raise_error()
109
109
 
110
- state.attributes[:foot].should == 'in mouth'
110
+ state.scope[:foot].should == 'in mouth'
111
111
  end
112
112
 
113
113
 
@@ -117,14 +117,18 @@ describe Inversion::RenderState do
117
117
  }.to raise_error( LocalJumpError, /no block/i )
118
118
  end
119
119
 
120
- it "provides accessor methods for its attributes" do
121
- state = Inversion::RenderState.new( :bar => :the_attribute_value )
122
- state.bar.should == :the_attribute_value
123
- end
120
+ describe Inversion::RenderState::Scope do
121
+
122
+ it "provides accessor methods for its attributes" do
123
+ state = Inversion::RenderState.new( :bar => :the_attribute_value )
124
+ state.scope.bar.should == :the_attribute_value
125
+ end
126
+
127
+ it "doesn't error if an accessor for a non-existant attribute is called" do
128
+ state = Inversion::RenderState.new( :bar => :the_attribute_value )
129
+ state.scope.foo.should be_nil()
130
+ end
124
131
 
125
- it "doesn't error if an accessor for a non-existant attribute is called" do
126
- state = Inversion::RenderState.new( :bar => :the_attribute_value )
127
- state.foo.should be_nil()
128
132
  end
129
133
 
130
134
  it "can be merged with another RenderState" do
@@ -132,8 +132,10 @@ describe Inversion::Template::AttrTag do
132
132
  end
133
133
 
134
134
  it "doesn't error if the attribute isn't set on the template" do
135
- state = Inversion::RenderState.new( :foo => nil )
136
- @tag.render( state ).should == nil
135
+ pending "recalling why we did this" do
136
+ state = Inversion::RenderState.new( :foo => nil )
137
+ @tag.render( state ).should == nil
138
+ end
137
139
  end
138
140
 
139
141
  it "can render itself as a comment for template debugging" do
@@ -199,6 +201,17 @@ describe Inversion::Template::AttrTag do
199
201
 
200
202
  template.render.should == "this is the result"
201
203
  end
204
+
205
+ it "renders method calls with template attribute arguments" do
206
+ template = Inversion::Template.new( 'this is <?attr foo.bar( baz ) ?>' )
207
+ foo = mock( "foo attribute object" )
208
+
209
+ template.foo = foo
210
+ template.baz = 18
211
+ foo.should_receive( :bar ).with( 18 ).and_return( "the result of calling bar" )
212
+
213
+ template.render.should == "this is the result of calling bar"
214
+ end
202
215
  end
203
216
 
204
217
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: inversion
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.1
4
+ version: 0.7.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -37,33 +37,33 @@ cert_chain:
37
37
  YUhDS0xaZFNLai9SSHVUT3QrZ2JsUmV4OEZBaDhOZUEKY21saFhlNDZwWk5K
38
38
  Z1dLYnhaYWg4NWpJang5NWhSOHZPSStOQU01aUg5a09xSzEzRHJ4YWNUS1Bo
39
39
  cWo1UGp3RgotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
40
- date: 2012-03-16 00:00:00.000000000 Z
40
+ date: 2012-03-30 00:00:00.000000000 Z
41
41
  dependencies:
42
42
  - !ruby/object:Gem::Dependency
43
43
  name: hoe-mercurial
44
- requirement: &70264152436760 !ruby/object:Gem::Requirement
44
+ requirement: &70356568944040 !ruby/object:Gem::Requirement
45
45
  none: false
46
46
  requirements:
47
47
  - - ~>
48
48
  - !ruby/object:Gem::Version
49
- version: 1.3.1
49
+ version: 1.4.0
50
50
  type: :development
51
51
  prerelease: false
52
- version_requirements: *70264152436760
52
+ version_requirements: *70356568944040
53
53
  - !ruby/object:Gem::Dependency
54
54
  name: hoe-manualgen
55
- requirement: &70264152436060 !ruby/object:Gem::Requirement
55
+ requirement: &70356568943280 !ruby/object:Gem::Requirement
56
56
  none: false
57
57
  requirements:
58
58
  - - ~>
59
59
  - !ruby/object:Gem::Version
60
- version: 0.2.0
60
+ version: 0.3.0
61
61
  type: :development
62
62
  prerelease: false
63
- version_requirements: *70264152436060
63
+ version_requirements: *70356568943280
64
64
  - !ruby/object:Gem::Dependency
65
65
  name: hoe-highline
66
- requirement: &70264152435540 !ruby/object:Gem::Requirement
66
+ requirement: &70356568942000 !ruby/object:Gem::Requirement
67
67
  none: false
68
68
  requirements:
69
69
  - - ~>
@@ -71,21 +71,21 @@ dependencies:
71
71
  version: 0.0.1
72
72
  type: :development
73
73
  prerelease: false
74
- version_requirements: *70264152435540
74
+ version_requirements: *70356568942000
75
75
  - !ruby/object:Gem::Dependency
76
76
  name: rdoc
77
- requirement: &70264152478140 !ruby/object:Gem::Requirement
77
+ requirement: &70356568957180 !ruby/object:Gem::Requirement
78
78
  none: false
79
79
  requirements:
80
80
  - - ~>
81
81
  - !ruby/object:Gem::Version
82
- version: '3.12'
82
+ version: '3.10'
83
83
  type: :development
84
84
  prerelease: false
85
- version_requirements: *70264152478140
85
+ version_requirements: *70356568957180
86
86
  - !ruby/object:Gem::Dependency
87
87
  name: rspec
88
- requirement: &70264152477360 !ruby/object:Gem::Requirement
88
+ requirement: &70356568956420 !ruby/object:Gem::Requirement
89
89
  none: false
90
90
  requirements:
91
91
  - - ~>
@@ -93,10 +93,10 @@ dependencies:
93
93
  version: '2.8'
94
94
  type: :development
95
95
  prerelease: false
96
- version_requirements: *70264152477360
96
+ version_requirements: *70356568956420
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: tilt
99
- requirement: &70264152476740 !ruby/object:Gem::Requirement
99
+ requirement: &70356568955640 !ruby/object:Gem::Requirement
100
100
  none: false
101
101
  requirements:
102
102
  - - ~>
@@ -104,10 +104,10 @@ dependencies:
104
104
  version: '1.3'
105
105
  type: :development
106
106
  prerelease: false
107
- version_requirements: *70264152476740
107
+ version_requirements: *70356568955640
108
108
  - !ruby/object:Gem::Dependency
109
109
  name: sinatra
110
- requirement: &70264152475940 !ruby/object:Gem::Requirement
110
+ requirement: &70356568954760 !ruby/object:Gem::Requirement
111
111
  none: false
112
112
  requirements:
113
113
  - - ~>
@@ -115,10 +115,10 @@ dependencies:
115
115
  version: '1.2'
116
116
  type: :development
117
117
  prerelease: false
118
- version_requirements: *70264152475940
118
+ version_requirements: *70356568954760
119
119
  - !ruby/object:Gem::Dependency
120
120
  name: rack-test
121
- requirement: &70264152474980 !ruby/object:Gem::Requirement
121
+ requirement: &70356568954020 !ruby/object:Gem::Requirement
122
122
  none: false
123
123
  requirements:
124
124
  - - ~>
@@ -126,10 +126,10 @@ dependencies:
126
126
  version: '0.6'
127
127
  type: :development
128
128
  prerelease: false
129
- version_requirements: *70264152474980
129
+ version_requirements: *70356568954020
130
130
  - !ruby/object:Gem::Dependency
131
131
  name: simplecov
132
- requirement: &70264152474040 !ruby/object:Gem::Requirement
132
+ requirement: &70356568953240 !ruby/object:Gem::Requirement
133
133
  none: false
134
134
  requirements:
135
135
  - - ~>
@@ -137,10 +137,10 @@ dependencies:
137
137
  version: '0.4'
138
138
  type: :development
139
139
  prerelease: false
140
- version_requirements: *70264152474040
140
+ version_requirements: *70356568953240
141
141
  - !ruby/object:Gem::Dependency
142
142
  name: trollop
143
- requirement: &70264152472820 !ruby/object:Gem::Requirement
143
+ requirement: &70356568952520 !ruby/object:Gem::Requirement
144
144
  none: false
145
145
  requirements:
146
146
  - - ~>
@@ -148,10 +148,10 @@ dependencies:
148
148
  version: '1.16'
149
149
  type: :development
150
150
  prerelease: false
151
- version_requirements: *70264152472820
151
+ version_requirements: *70356568952520
152
152
  - !ruby/object:Gem::Dependency
153
153
  name: highline
154
- requirement: &70264152471760 !ruby/object:Gem::Requirement
154
+ requirement: &70356568951720 !ruby/object:Gem::Requirement
155
155
  none: false
156
156
  requirements:
157
157
  - - ~>
@@ -159,10 +159,10 @@ dependencies:
159
159
  version: '1.6'
160
160
  type: :development
161
161
  prerelease: false
162
- version_requirements: *70264152471760
162
+ version_requirements: *70356568951720
163
163
  - !ruby/object:Gem::Dependency
164
164
  name: sysexits
165
- requirement: &70264152471180 !ruby/object:Gem::Requirement
165
+ requirement: &70356568950980 !ruby/object:Gem::Requirement
166
166
  none: false
167
167
  requirements:
168
168
  - - ~>
@@ -170,18 +170,18 @@ dependencies:
170
170
  version: '1.0'
171
171
  type: :development
172
172
  prerelease: false
173
- version_requirements: *70264152471180
173
+ version_requirements: *70356568950980
174
174
  - !ruby/object:Gem::Dependency
175
175
  name: hoe
176
- requirement: &70264152486800 !ruby/object:Gem::Requirement
176
+ requirement: &70356568950160 !ruby/object:Gem::Requirement
177
177
  none: false
178
178
  requirements:
179
179
  - - ~>
180
180
  - !ruby/object:Gem::Version
181
- version: '2.16'
181
+ version: '3.0'
182
182
  type: :development
183
183
  prerelease: false
184
- version_requirements: *70264152486800
184
+ version_requirements: *70356568950160
185
185
  description: ! 'Inversion is a templating system for Ruby. It uses the "Inversion
186
186
  of Control"
187
187
 
@@ -288,8 +288,10 @@ licenses:
288
288
  - BSD
289
289
  post_install_message:
290
290
  rdoc_options:
291
- - --main
292
- - README.rdoc
291
+ - -f
292
+ - fivefish
293
+ - -t
294
+ - Inversion Templates
293
295
  require_paths:
294
296
  - lib
295
297
  required_ruby_version: !ruby/object:Gem::Requirement
metadata.gz.sig CHANGED
Binary file