inversion 1.0.0 → 1.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/History.rdoc +46 -0
  5. data/Manifest.txt +0 -1
  6. data/README.rdoc +10 -4
  7. data/Rakefile +4 -90
  8. data/bin/inversion +3 -1
  9. data/lib/inversion.rb +4 -4
  10. data/lib/inversion/command.rb +2 -1
  11. data/lib/inversion/exceptions.rb +4 -1
  12. data/lib/inversion/mixins.rb +30 -4
  13. data/lib/inversion/monkeypatches.rb +3 -2
  14. data/lib/inversion/parser.rb +2 -2
  15. data/lib/inversion/renderstate.rb +2 -1
  16. data/lib/inversion/sinatra.rb +4 -1
  17. data/lib/inversion/template.rb +3 -3
  18. data/lib/inversion/template/attrtag.rb +8 -6
  19. data/lib/inversion/template/begintag.rb +3 -1
  20. data/lib/inversion/template/calltag.rb +3 -1
  21. data/lib/inversion/template/codetag.rb +13 -2
  22. data/lib/inversion/template/commenttag.rb +3 -1
  23. data/lib/inversion/template/configtag.rb +10 -2
  24. data/lib/inversion/template/containertag.rb +2 -2
  25. data/lib/inversion/template/defaulttag.rb +4 -2
  26. data/lib/inversion/template/elsetag.rb +3 -1
  27. data/lib/inversion/template/elsiftag.rb +36 -7
  28. data/lib/inversion/template/endtag.rb +3 -3
  29. data/lib/inversion/template/escapetag.rb +3 -1
  30. data/lib/inversion/template/fortag.rb +14 -8
  31. data/lib/inversion/template/fragmenttag.rb +3 -1
  32. data/lib/inversion/template/iftag.rb +37 -8
  33. data/lib/inversion/template/importtag.rb +4 -2
  34. data/lib/inversion/template/includetag.rb +3 -1
  35. data/lib/inversion/template/node.rb +2 -1
  36. data/lib/inversion/template/pptag.rb +4 -2
  37. data/lib/inversion/template/publishtag.rb +3 -1
  38. data/lib/inversion/template/rescuetag.rb +3 -1
  39. data/lib/inversion/template/subscribetag.rb +3 -1
  40. data/lib/inversion/template/tag.rb +4 -4
  41. data/lib/inversion/template/textnode.rb +3 -1
  42. data/lib/inversion/template/timedeltatag.rb +16 -3
  43. data/lib/inversion/template/unlesstag.rb +31 -3
  44. data/lib/inversion/template/uriencodetag.rb +5 -5
  45. data/lib/inversion/template/yieldtag.rb +3 -1
  46. data/lib/inversion/tilt.rb +3 -1
  47. data/spec/helpers.rb +3 -0
  48. data/spec/inversion/mixins_spec.rb +14 -14
  49. data/spec/inversion/renderstate_spec.rb +0 -9
  50. data/spec/inversion/template/codetag_spec.rb +41 -1
  51. data/spec/inversion/template/elsiftag_spec.rb +25 -0
  52. data/spec/inversion/template/fortag_spec.rb +34 -2
  53. data/spec/inversion/template/iftag_spec.rb +11 -0
  54. data/spec/inversion/template/pptag_spec.rb +2 -0
  55. data/spec/inversion/template/timedeltatag_spec.rb +7 -0
  56. data/spec/inversion/template/unlesstag_spec.rb +11 -0
  57. data/spec/inversion/template_spec.rb +2 -6
  58. metadata +84 -121
  59. metadata.gz.sig +0 -0
  60. data/ChangeLog +0 -2078
@@ -1,6 +1,8 @@
1
- #!/usr/bin/env ruby
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
2
3
  # vim: set noet nosta sw=4 ts=4 :
3
4
 
5
+ require 'inversion/template' unless defined?( Inversion::Template )
4
6
  require 'inversion/template/node'
5
7
 
6
8
  # Inversion text node class -- container for static content in templates between tags.
@@ -1,11 +1,15 @@
1
- #!/usr/bin/env ruby
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
2
3
  # vim: set noet nosta sw=4 ts=4 :
3
4
 
4
5
  require 'uri'
5
6
  require 'time'
6
7
  require 'date'
8
+
9
+ require 'inversion/template' unless defined?( Inversion::Template )
7
10
  require 'inversion/template/attrtag'
8
11
 
12
+
9
13
  # Inversion time delta tag.
10
14
  #
11
15
  # This tag is a derivative of the 'attr' tag that transforms the results of its method call
@@ -37,6 +41,11 @@ class Inversion::Template::TimeDeltaTag < Inversion::Template::AttrTag
37
41
  def render( renderstate )
38
42
  val = super( renderstate )
39
43
  time = nil
44
+ omit_decorator = false
45
+
46
+ if val.respond_to?( :key )
47
+ val, omit_decorator = val.values_at( :time, :omit_decorator )
48
+ end
40
49
 
41
50
  if val.respond_to?( :to_time )
42
51
  time = val.to_time
@@ -49,10 +58,14 @@ class Inversion::Template::TimeDeltaTag < Inversion::Template::AttrTag
49
58
  now = Time.now
50
59
  if now > time
51
60
  seconds = now - time
52
- return "%s ago" % [ timeperiod(seconds) ]
61
+ period = timeperiod( seconds )
62
+ period += ' ago' unless omit_decorator
63
+ return period
53
64
  else
54
65
  seconds = time - now
55
- return "%s from now" % [ timeperiod(seconds) ]
66
+ period = timeperiod( seconds )
67
+ period += ' from now' unless omit_decorator
68
+ return period
56
69
  end
57
70
  end
58
71
 
@@ -1,7 +1,9 @@
1
- #!/usr/bin/env ruby
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
2
3
  # vim: set noet nosta sw=4 ts=4 :
3
4
 
4
5
  require 'inversion/mixins'
6
+ require 'inversion/template' unless defined?( Inversion::Template )
5
7
  require 'inversion/template/attrtag'
6
8
  require 'inversion/template/containertag'
7
9
  require 'inversion/template/elsetag'
@@ -20,14 +22,40 @@ require 'inversion/template/elsetag'
20
22
  class Inversion::Template::UnlessTag < Inversion::Template::AttrTag
21
23
  include Inversion::Template::ContainerTag
22
24
 
23
- # Inherits AttrTag's tag patterns
25
+ # Inherit AttrTag's tag patterns first.
26
+ inherit_tag_patterns
27
+
28
+ # Append a 'not' tag matcher.
29
+ # <?unless ! foo ?>, <?unless !foo ?>
30
+ tag_pattern '$(op) sp* $(ident)' do |tag, match|
31
+ op = match.string( 1 )
32
+ raise Inversion::ParseError, "expected '!', got %p instead" % [ op ] unless op == '!'
33
+
34
+ tag.send( :log ).debug " Identifier is: %p (inverted)" % [ match.string(2) ]
35
+ tag.name = match.string( 2 ).to_sym
36
+ tag.inverted = true
37
+ end
38
+
39
+
40
+ ### Create a new UnlessTag.
41
+ def initialize( body, linenum=nil, colnum=nil )
42
+ @inverted = false
43
+ super
44
+ end
45
+
46
+ # Invert the tag's renderstate if created with the 'not' operator.
47
+ attr_accessor :inverted
48
+
24
49
 
25
50
  ### Render the tag's contents if the condition is true, or any else or elsif sections
26
51
  ### if the condition isn't true.
27
52
  def render( state )
28
53
 
54
+ evaluated_state = self.evaluate( state )
55
+ evaluated_state = ! evaluated_state if self.inverted
56
+
29
57
  # Start out with rendering *disabled* if the tag body evaluates trueishly
30
- if self.evaluate( state )
58
+ if evaluated_state
31
59
  self.log.debug "Initial state was TRUE; disabling rendering"
32
60
  state.disable_rendering
33
61
  else
@@ -1,7 +1,9 @@
1
- #!/usr/bin/env ruby
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
2
3
  # vim: set noet nosta sw=4 ts=4 :
3
4
 
4
5
  require 'uri'
6
+ require 'inversion/template' unless defined?( Inversion::Template )
5
7
  require 'inversion/template/attrtag'
6
8
 
7
9
  # Inversion URL encoding tag.
@@ -14,16 +16,14 @@ require 'inversion/template/attrtag'
14
16
  # <?uriencode foo.bar ?>
15
17
  #
16
18
  class Inversion::Template::UriencodeTag < Inversion::Template::AttrTag
19
+ include Inversion::Escaping
17
20
 
18
- # Unreserved characters from section 2.3 of RFC 3986
19
- # ALPHA / DIGIT / "-" / "." / "_" / "~"
20
- DEFAULT_ENCODED_CHARACTERS = /[^\w\-\.~]/
21
21
 
22
22
  ### Render the method chains against the attributes of the specified +render_state+
23
23
  ### and return them.
24
24
  def render( render_state )
25
25
  raw = super
26
- return URI.encode( raw.to_s, DEFAULT_ENCODED_CHARACTERS )
26
+ return escape_uri( raw.to_s )
27
27
  end
28
28
 
29
29
  end # class Inversion::Template::UriencodeTag
@@ -1,8 +1,10 @@
1
- #!/usr/bin/env ruby
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
2
3
  # vim: set noet nosta sw=4 ts=4 :
3
4
 
4
5
  require 'pathname'
5
6
  require 'inversion/mixins'
7
+ require 'inversion/template' unless defined?( Inversion::Template )
6
8
  require 'inversion/template/tag'
7
9
 
8
10
 
@@ -1,4 +1,6 @@
1
- #!/usr/bin/env ruby
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+ # vim: set noet nosta sw=4 ts=4 :
2
4
 
3
5
  require 'tilt'
4
6
 
@@ -5,6 +5,7 @@
5
5
  require 'simplecov' if ENV['COVERAGE']
6
6
 
7
7
  require 'rspec'
8
+ require 'rspec/wait'
8
9
  require 'loggability'
9
10
  require 'loggability/spechelpers'
10
11
 
@@ -44,6 +45,8 @@ RSpec.configure do |c|
44
45
  c.mock_with( :rspec ) do |mock|
45
46
  mock.syntax = :expect
46
47
  end
48
+ c.wait_timeout = 3
49
+ c.example_status_persistence_file_path = 'spec/.state'
47
50
 
48
51
  c.include( Inversion::SpecHelpers )
49
52
  c.include( Loggability::SpecHelpers )
@@ -108,7 +108,7 @@ describe Inversion, "mixins" do
108
108
  include Inversion::Escaping
109
109
 
110
110
  def render( state )
111
- return self.escape( "<something>", state )
111
+ return self.escape( "</something>", state )
112
112
  end
113
113
  end
114
114
  @obj = objclass.new
@@ -116,18 +116,28 @@ describe Inversion, "mixins" do
116
116
 
117
117
  it "adds configurable escaping to including classes" do
118
118
  render_state = Inversion::RenderState.new( {}, :escape_format => :html )
119
- expect( @obj.render( render_state ) ).to eq( "&lt;something&gt;" )
119
+ expect( @obj.render( render_state ) ).to eq( "&lt;/something&gt;" )
120
120
  end
121
121
 
122
122
  it "doesn't escape anything if escaping is disabled" do
123
123
  render_state = Inversion::RenderState.new( {}, :escape_format => nil )
124
- expect( @obj.render( render_state ) ).to eq( "<something>" )
124
+ expect( @obj.render( render_state ) ).to eq( "</something>" )
125
125
  end
126
126
 
127
127
  it "doesn't escape anything if escaping is set to ':none'" do
128
128
  render_state = Inversion::RenderState.new( {}, :escape_format => :none )
129
- expect( @obj.render( render_state ) ).to eq( "<something>" )
129
+ expect( @obj.render( render_state ) ).to eq( "</something>" )
130
130
  end
131
+
132
+ it "supports URI escaping" do
133
+ render_state = Inversion::RenderState.new( {}, :escape_format => :uri )
134
+
135
+ rval = @obj.render( render_state )
136
+
137
+ expect( rval.encoding ).to eq( Encoding::US_ASCII )
138
+ expect( rval ).to eq( "%3C%2Fsomething%3E" )
139
+ end
140
+
131
141
  end
132
142
 
133
143
 
@@ -235,16 +245,6 @@ describe Inversion, "mixins" do
235
245
  expect( copy.default_proc ).to eq( original.default_proc )
236
246
  end
237
247
 
238
- it "preserves taintedness of copied objects" do
239
- original = Object.new
240
- original.taint
241
-
242
- copy = Inversion::DataUtilities.deep_copy( original )
243
-
244
- expect( copy ).to_not be( original )
245
- expect( copy ).to be_tainted()
246
- end
247
-
248
248
  it "preserves frozen-ness of copied objects" do
249
249
  original = Object.new
250
250
  original.freeze
@@ -41,15 +41,6 @@ describe Inversion::RenderState do
41
41
  expect( state.scope[:bear] ).to_not equal( attributes[:bear] )
42
42
  end
43
43
 
44
- it "preserves tainted status when copying its attributes" do
45
- attributes = { :danger => "in pants" }
46
- attributes[:danger].taint
47
-
48
- state = Inversion::RenderState.new( attributes )
49
-
50
- expect( state.scope[:danger] ).to be_tainted()
51
- end
52
-
53
44
  it "preserves singleton methods on attribute objects when copying" do
54
45
  obj = Object.new
55
46
  def obj.foo; "foo!"; end
@@ -28,6 +28,46 @@ describe Inversion::Template::CodeTag do
28
28
  expect( subclass.tag_patterns.first[1].call(:dummy, :king_dummy) ).to eq( :foo )
29
29
  end
30
30
 
31
- end
32
31
 
32
+ it "can explicitly declare pattern inheritence" do
33
+ parentclass = Class.new( Inversion::Template::CodeTag ) do
34
+ tag_pattern "$(ident)" do |tag, match|
35
+ :foo
36
+ end
37
+ end
38
+
39
+ subclass = Class.new( parentclass ) do
40
+ inherit_tag_patterns
41
+ tag_pattern "$(op) $(ident)" do |tag, match|
42
+ :bar
43
+ end
44
+ end
45
+
46
+ expect( subclass.tag_patterns.size ).to eq( 2 )
47
+ expect( subclass.tag_patterns.first[0] ).
48
+ to be_an_instance_of( Inversion::Template::CodeTag::TokenPattern )
49
+ expect( subclass.tag_patterns.last[0] ).
50
+ to be_an_instance_of( Inversion::Template::CodeTag::TokenPattern )
51
+ expect( subclass.tag_patterns.first[1].call(:dummy, :king_dummy) ).to eq( :foo )
52
+ expect( subclass.tag_patterns.last[1].call(:dummy, :king_dummy) ).to eq( :bar )
53
+ end
54
+
55
+
56
+ it "throws an error if trying to inherit patterns after they are declared" do
57
+ parentclass = Class.new( Inversion::Template::CodeTag ) do
58
+ tag_pattern "$(ident)" do |tag, match|
59
+ :foo
60
+ end
61
+ end
62
+
63
+ expect {
64
+ Class.new( parentclass ) do
65
+ tag_pattern "$(op) $(ident)" do |tag, match|
66
+ :bar
67
+ end
68
+ inherit_tag_patterns
69
+ end
70
+ }.to raise_exception( ScriptError, /patterns already exist/i )
71
+ end
72
+ end
33
73
  end
@@ -58,6 +58,31 @@ describe Inversion::Template::ElsifTag do
58
58
  end
59
59
 
60
60
 
61
+ it "inverts its attribute with a ! operator" do
62
+ template = Inversion::Template.new( <<-END_TEMPLATE )
63
+ <?if thing ?>
64
+ Thing!
65
+ <?elsif ! otherthing ?>
66
+ Otherthing!
67
+ <?else ?>
68
+ Nope.
69
+ <?end?>
70
+ END_TEMPLATE
71
+
72
+ template.thing = true
73
+ template.otherthing = true
74
+ expect( template.render ).to include( "Thing!" )
75
+
76
+ template.thing = false
77
+ template.otherthing = false
78
+ expect( template.render ).to include( "Otherthing!" )
79
+
80
+ template.thing = false
81
+ template.otherthing = true
82
+ expect( template.render ).to include( "Nope." )
83
+ end
84
+
85
+
61
86
  it "renders as its attribute value if it's a simple attribute" do
62
87
  renderstate = Inversion::RenderState.new( :bar => :the_attribute_value )
63
88
  tag = Inversion::Template::ElsifTag.new( 'bar' )
@@ -11,12 +11,12 @@ require 'inversion/renderstate'
11
11
 
12
12
  describe Inversion::Template::ForTag do
13
13
 
14
-
15
14
  it "knows which identifiers should be added to the template" do
16
15
  tag = Inversion::Template::ForTag.new( 'foo in bar' )
17
16
  expect( tag.identifiers ).to eq( [ :bar ] )
18
17
  end
19
18
 
19
+
20
20
  it "can iterate over single items of a collection attribute" do
21
21
  tag = Inversion::Template::ForTag.new( 'foo in bar' )
22
22
 
@@ -24,6 +24,7 @@ describe Inversion::Template::ForTag do
24
24
  expect( tag.enumerator ).to eq( 'bar' )
25
25
  end
26
26
 
27
+
27
28
  it "should render as nothing if the corresponding attribute in the template is unset" do
28
29
  render_state = Inversion::RenderState.new( :bar => nil )
29
30
 
@@ -38,6 +39,7 @@ describe Inversion::Template::ForTag do
38
39
  expect( tag.render( render_state ) ).to be_nil()
39
40
  end
40
41
 
42
+
41
43
  it "renders each of its subnodes for each iteration, replacing its " +
42
44
  "block arguments with the yielded values" do
43
45
  render_state = Inversion::RenderState.new( :bar => %w[monkey goat] )
@@ -54,6 +56,7 @@ describe Inversion::Template::ForTag do
54
56
  expect( render_state.to_s ).to eq( "[monkey][goat]" )
55
57
  end
56
58
 
59
+
57
60
  it "supports nested iterators" do
58
61
  render_state = Inversion::RenderState.new( :tic => [ 'x', 'o'], :tac => ['o', 'x'] )
59
62
 
@@ -74,6 +77,7 @@ describe Inversion::Template::ForTag do
74
77
  expect( render_state.to_s ).to eq( "[x, o][x, x][o, o][o, x]" )
75
78
  end
76
79
 
80
+
77
81
  it "supports iterating over a range" do
78
82
 
79
83
  # <?for omarker in tic ?><?for imarker in tac ?>
@@ -87,12 +91,14 @@ describe Inversion::Template::ForTag do
87
91
  expect( render_state.to_s ).to eq( "0 1 2 3 4 5 6 7 8 9 10 " )
88
92
  end
89
93
 
94
+
90
95
  it "raises a ParseError if a keyword other than 'in' is used" do
91
96
  expect {
92
97
  Inversion::Template::ForTag.new( 'foo begin bar' )
93
98
  }.to raise_error( Inversion::ParseError, /invalid/i )
94
99
  end
95
100
 
101
+
96
102
  context "multidimensional collections" do
97
103
 
98
104
  it "can be expanded into multiple block arguments" do
@@ -103,6 +109,27 @@ describe Inversion::Template::ForTag do
103
109
  end
104
110
 
105
111
 
112
+ it "can be expanded by nesting" do
113
+ tmpl = Inversion::Template.new( <<-"END_TEMPLATE" )
114
+ <?for outer in numbers ?>
115
+ -
116
+ <?for inner in outer ?>
117
+ :<?attr inner ?>:
118
+ <?end for ?>
119
+ -
120
+ <?end for ?>
121
+ END_TEMPLATE
122
+
123
+ tmpl.numbers = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10]]
124
+ results = ''
125
+ wait_for { results = tmpl.render }.to be_a( String )
126
+
127
+ expect( results ).to match( /-\s+:1:\s+:2:\s+:3:\s*:4:\s+-/ )
128
+ expect( results ).to match( /-\s+:5:\s+:6:\s+:7:\s*:8:\s+-/ )
129
+ expect( results ).to match( /-\s+:9:\s+:10:\s+-/ )
130
+ end
131
+
132
+
106
133
  it "can be expanded into multiple block arguments (sans spaces)" do
107
134
  tag = Inversion::Template::ForTag.new( 'splip,splorp,sploop in splap' )
108
135
 
@@ -110,6 +137,7 @@ describe Inversion::Template::ForTag do
110
137
  expect( tag.enumerator ).to eq( 'splap' )
111
138
  end
112
139
 
140
+
113
141
  it "can be expanded into multiple block arguments from hash pairs" do
114
142
  tag = Inversion::Template::ForTag.new( 'key, value in splap' )
115
143
 
@@ -129,6 +157,7 @@ describe Inversion::Template::ForTag do
129
157
  expect( render_state.to_s ).to eq( '[one translates to uno][two translates to dos]' )
130
158
  end
131
159
 
160
+
132
161
  it "can be expanded into multiple block arguments with complex values" do
133
162
  # [<?attr key?> translates to <?attr value?>]
134
163
  tree = Inversion::Parser.new( nil ).parse( <<-"END_TEMPLATE" )
@@ -153,6 +182,7 @@ describe Inversion::Template::ForTag do
153
182
  expect( render_state.to_s ).to match( /ch \(1\) => ch/ )
154
183
  end
155
184
 
185
+
156
186
  it "preserves an array of subhashes" do
157
187
  tree = Inversion::Parser.new( nil ).parse( <<-"END_TEMPLATE" )
158
188
  <?for subhash in the_hash[:a] ?>
@@ -168,7 +198,7 @@ describe Inversion::Template::ForTag do
168
198
  render_state = Inversion::RenderState.new( :the_hash => the_hash )
169
199
  tree.first.render( render_state )
170
200
 
171
- expect( render_state.to_s ).to match( /Subhash is a Hash/i )
201
+ expect( render_state.to_s ).to match( /subhash is a hash.*subhash is a hash/i )
172
202
  end
173
203
 
174
204
  end
@@ -245,7 +275,9 @@ describe Inversion::Template::ForTag do
245
275
  </section>
246
276
  END_OUTPUT
247
277
  end
278
+
248
279
  end
280
+
249
281
  end
250
282
 
251
283