lotus-view 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e8fa1effeef34385f8e2500ab74842e8d196f9bf
4
- data.tar.gz: 203e83425ed18d487d19cc825443fed66c53c86e
3
+ metadata.gz: 500bbc3ad33d7a944aa35980d6257df6c9653c01
4
+ data.tar.gz: 319d161fcdf16cbe089df0e178d8b07b5fd9ee09
5
5
  SHA512:
6
- metadata.gz: 6b1317306b81d7579d3a3894ba2f22584f69f28039a0161a706c3d3cb7f89cc5a2b591c09e7c57b684d53870a9b96010a8db882717cd8a6ce0d9574f59be4b81
7
- data.tar.gz: bee5b8b82b89c9e561bc89bffb53ba3a01028393934fb8bfa350e7fb13c240a305061a3c9b8196abadb710c13d3654b932a4986a609b06280c49c7abc24c6af0
6
+ metadata.gz: 0b99d764d671bdf5b5ad821f24aa4d1958f3e4fe92062018fc70b66c81dc799e8a3ad014c9f93e676e195485f4d174bb8e339a45be71a4b7f890f639d9509e8e
7
+ data.tar.gz: 1852df5e30fc12155bafb9c4a5ff23def34895b4ec376cceb2ef7d4adf57f6b2f109984a1d71a07a753bb2ede8291bac70b88989742a015d25998b1262cb98b0
data/CHANGELOG.md CHANGED
@@ -1,6 +1,15 @@
1
1
  # Lotus::View
2
2
  View layer for Lotus
3
3
 
4
+ ## v0.4.0 - 2015-03-23
5
+ ### Changed
6
+ - [Luca Guidi] Autoescape concrete and virtual methods from presenters
7
+ - [Luca Guidi] Autoescape concrete and virtual methods from views
8
+
9
+ ### Fixed
10
+ - [Tom Kadwill] Improve error message for undefined method in view
11
+ - [Luca Guidi] Ensure that layouts will include modules from `Configuration#prepare`
12
+
4
13
  ## v0.3.0 - 2014-12-23
5
14
  ### Added
6
15
  - [Trung Lê] When duplicate the framework, also duplicate `Presenter`
data/README.md CHANGED
@@ -366,6 +366,10 @@ For instance, if [ERubis](http://www.kuwata-lab.com/erubis/) is loaded, it will
366
366
  <td>Scss</td>
367
367
  <td>scss</td>
368
368
  </tr>
369
+ <tr>
370
+ <td>Slim</td>
371
+ <td>slim</td>
372
+ </tr>
369
373
  <tr>
370
374
  <td>String</td>
371
375
  <td>str</td>
@@ -622,6 +626,171 @@ After this operation, all the class variables are frozen, in order to prevent ac
622
626
 
623
627
  **This is not necessary, when Lotus::View is used within a Lotus application.**
624
628
 
629
+ ### Security
630
+
631
+ The output of views and presenters is always **autoescaped**.
632
+
633
+ **ATTENTION:** In order to prevent XSS attacks, please read the instructions below.
634
+ Because Lotus::View supports a lot of template engines, the escape happens at the level of the view.
635
+ Most of the time everything happens automatically, but there are still some corner cases that need your manual intervention.
636
+
637
+ #### View autoescape
638
+
639
+ ```ruby
640
+ require 'lotus/view'
641
+
642
+ User = Struct.new(:name)
643
+
644
+ module Users
645
+ class Show
646
+ include Lotus::View
647
+
648
+ def user_name
649
+ user.name
650
+ end
651
+ end
652
+ end
653
+
654
+ # ERB template
655
+ # <div id="user_name"><%= user_name %></div>
656
+
657
+ user = User.new("<script>alert('xss')</script>")
658
+
659
+ # THIS IS USEFUL FOR UNIT TESTING:
660
+ template = Lotus::View::Template.new('users/show.html.erb')
661
+ view = Users::Show.new(template, user: user)
662
+ view.user_name # => "&lt;script&gt;alert(&apos;xss&apos;)&lt;&#x2F;script&gt;"
663
+
664
+ # THIS IS THE RENDERING OUTPUT:
665
+ Users::Show.render(format: :html, user: user)
666
+ # => <div id="user_name">&lt;script&gt;alert(&apos;xss&apos;)&lt;&#x2F;script&gt;</div>
667
+ ```
668
+
669
+ #### Presenter autoescape
670
+
671
+ ```ruby
672
+ require 'lotus/view'
673
+
674
+ User = Struct.new(:name)
675
+
676
+ class UserPresenter
677
+ include Lotus::Presenter
678
+ end
679
+
680
+ user = User.new("<script>alert('xss')</script>")
681
+ presenter = UserPresenter.new(user)
682
+
683
+ presenter.name # => "&lt;script&gt;alert(&apos;xss&apos;)&lt;&#x2F;script&gt;"
684
+ ```
685
+
686
+ #### Escape entire objects
687
+
688
+ We have seen that concrete methods in views are automatically escaped.
689
+ This is great, but tedious if you need to print a lot of information from a given object.
690
+
691
+ Imagine you have `user` as part of the view locals.
692
+ If you want to use `<%= user.name %>` directly, **you're still vulnerable to XSS attacks**.
693
+
694
+ You have two alternatives:
695
+
696
+ * To use a concrete presenter (eg. `UserPresenter`)
697
+ * Escape the entire object (see the example below)
698
+
699
+ Both those solutions allow you to keep the template syntax unchanged, but to have a safer output.
700
+
701
+ ```ruby
702
+ require 'lotus/view'
703
+
704
+ User = Struct.new(:first_name, :last_name)
705
+
706
+ module Users
707
+ class Show
708
+ include Lotus::View
709
+
710
+ def user
711
+ _escape locals[:user]
712
+ end
713
+ end
714
+ end
715
+
716
+ # ERB template:
717
+ #
718
+ # <div id="first_name">
719
+ # <%= user.first_name %>
720
+ # </div>
721
+ # <div id="last_name">
722
+ # <%= user.last_name %>
723
+ # </div>
724
+
725
+ first_name = "<script>alert('first_name')</script>"
726
+ last_name = "<script>alert('last_name')</script>"
727
+
728
+ user = User.new(first_name, last_name)
729
+ html = Users::Show.render(format: :html, user: user)
730
+
731
+ html
732
+ # =>
733
+ # <div id="first_name">
734
+ # &lt;script&gt;alert(&apos;first_name&apos;)&lt;&#x2F;script&gt;
735
+ # </div>
736
+ # <div id="last_name">
737
+ # &lt;script&gt;alert(&apos;last_name&apos;)&lt;&#x2F;script&gt;
738
+ # </div>
739
+ ```
740
+
741
+ #### Raw contents
742
+
743
+ You can use `_raw` to mark an output as safe.
744
+ Please note that **this may open your application to XSS attacks.**
745
+
746
+ #### Raw contents in views
747
+
748
+ ```ruby
749
+ require 'lotus/view'
750
+
751
+ User = Struct.new(:name)
752
+
753
+ module Users
754
+ class Show
755
+ include Lotus::View
756
+
757
+ def user_name
758
+ _raw user.name
759
+ end
760
+ end
761
+ end
762
+
763
+ # ERB template
764
+ # <div id="user_name"><%= user_name %></div>
765
+
766
+ user = User.new("<script>alert('xss')</script>")
767
+ html = Users::Show.render(format: :html, user: user)
768
+
769
+ html
770
+ # => <div id="user_name"><script>alert('xss')</script></div>
771
+ ```
772
+
773
+ #### Raw contents in presenters
774
+
775
+ ```ruby
776
+ require 'lotus/view'
777
+
778
+ User = Struct.new(:name)
779
+
780
+ class UserPresenter
781
+ include Lotus::Presenter
782
+
783
+ def first_name
784
+ _raw @object.first_name
785
+ end
786
+ end
787
+
788
+ user = User.new("<script>alert('xss')</script>")
789
+ presenter = UserPresenter.new(user)
790
+
791
+ presenter.name # => "<script>alert('xss')</script>"
792
+ ```
793
+
625
794
  ## Versioning
626
795
 
627
796
  __Lotus::View__ uses [Semantic Versioning 2.0.0](http://semver.org)
@@ -636,4 +805,4 @@ __Lotus::View__ uses [Semantic Versioning 2.0.0](http://semver.org)
636
805
 
637
806
  ## Copyright
638
807
 
639
- Copyright 2014 Luca Guidi – Released under MIT License
808
+ Copyright 2014-2015 Luca Guidi – Released under MIT License
data/lib/lotus/layout.rb CHANGED
@@ -34,6 +34,8 @@ module Lotus
34
34
 
35
35
  self.configuration = conf.duplicate
36
36
  end
37
+
38
+ conf.copy!(base)
37
39
  end
38
40
 
39
41
  # Class level API
@@ -1,9 +1,15 @@
1
+ require 'lotus/view/escape'
2
+
1
3
  module Lotus
2
4
  # Presenter pattern implementation
3
5
  #
6
+ # It delegates to the wrapped object the missing method invocations.
7
+ #
8
+ # The output of concrete and delegated methods is escaped as XSS prevention.
9
+ #
4
10
  # @since 0.1.0
5
11
  #
6
- # @example
12
+ # @example Basic usage
7
13
  # require 'lotus/view'
8
14
  #
9
15
  # class Map
@@ -48,7 +54,45 @@ module Lotus
48
54
  #
49
55
  # # it has private access to the original object
50
56
  # puts presenter.inspect_object # => #<Map:0x007fdeada0b2f0 @locations=["Rome", "Boston"]>
57
+ #
58
+ # @example Escape
59
+ # require 'lotus/view'
60
+ #
61
+ # User = Struct.new(:first_name, :last_name)
62
+ #
63
+ # class UserPresenter
64
+ # include Lotus::Presenter
65
+ #
66
+ # def full_name
67
+ # [first_name, last_name].join(' ')
68
+ # end
69
+ #
70
+ # def raw_first_name
71
+ # _raw first_name
72
+ # end
73
+ # end
74
+ #
75
+ # first_name = '<script>alert('xss')</script>'
76
+ #
77
+ # user = User.new(first_name, nil)
78
+ # presenter = UserPresenter.new(user)
79
+ #
80
+ # presenter.full_name
81
+ # # => "&lt;script&gt;alert(&apos;xss&apos;)&lt;&#x2F;script&gt;"
82
+ #
83
+ # presenter.raw_full_name
84
+ # # => "<script>alert('xss')</script>"
51
85
  module Presenter
86
+ # Inject escape logic into the given class.
87
+ #
88
+ # @since 0.4.0
89
+ # @api private
90
+ #
91
+ # @see Lotus::View::Escape
92
+ def self.included(base)
93
+ base.extend ::Lotus::View::Escape
94
+ end
95
+
52
96
  # Initialize the presenter
53
97
  #
54
98
  # @param object [Object] the object to present
@@ -65,7 +109,7 @@ module Lotus
65
109
  # @since 0.1.0
66
110
  def method_missing(m, *args, &blk)
67
111
  if @object.respond_to?(m)
68
- @object.__send__ m, *args, &blk
112
+ ::Lotus::View::Escape.html(@object.__send__(m, *args, &blk))
69
113
  else
70
114
  super
71
115
  end
data/lib/lotus/view.rb CHANGED
@@ -5,6 +5,7 @@ require 'lotus/view/version'
5
5
  require 'lotus/view/configuration'
6
6
  require 'lotus/view/inheritable'
7
7
  require 'lotus/view/rendering'
8
+ require 'lotus/view/escape'
8
9
  require 'lotus/view/dsl'
9
10
  require 'lotus/layout'
10
11
  require 'lotus/presenter'
@@ -270,6 +271,7 @@ module Lotus
270
271
  extend Inheritable.dup
271
272
  extend Dsl.dup
272
273
  extend Rendering.dup
274
+ extend Escape.dup
273
275
 
274
276
  include Utils::ClassAttribute
275
277
  class_attribute :configuration
@@ -212,12 +212,13 @@ module Lotus
212
212
  end
213
213
 
214
214
  # When a value is given, it specifies the layout.
215
+ # When false is given, Lotus::View::Rendering::NullLayout is returned.
215
216
  # Otherwise, it returns the previously specified layout.
216
217
  #
217
218
  # When the global configuration is set (`Lotus::View.layout=`), after the
218
219
  # loading process, it will return that layout if not otherwise specified.
219
220
  #
220
- # @param value [Symbol,nil] the layout name
221
+ # @param value [Symbol, FalseClass, nil] the layout name
221
222
  #
222
223
  # @return [Symbol, nil] the specified layout for this view, if set
223
224
  #
@@ -294,9 +295,28 @@ module Lotus
294
295
  #
295
296
  # Lotus::View.load!
296
297
  # Articles::Show.layout # => :articles
298
+ #
299
+ # @example Disable layout for the view
300
+ # require 'lotus/view'
301
+ #
302
+ # class ApplicationLayout
303
+ # include Lotus::Layout
304
+ # end
305
+ #
306
+ # module Articles
307
+ # class Show
308
+ # include Lotus::View
309
+ # layout false
310
+ # end
311
+ # end
312
+ #
313
+ # Lotus::View.load!
314
+ # Articles::Show.layout # => Lotus::View::Rendering::NullLayout
297
315
  def layout(value = nil)
298
316
  if value.nil?
299
317
  @_layout ||= Rendering::LayoutFinder.find(@layout || configuration.layout, configuration.namespace)
318
+ elsif !value
319
+ @layout = Lotus::View::Rendering::NullLayout
300
320
  else
301
321
  @layout = value
302
322
  end
@@ -0,0 +1,180 @@
1
+ require 'lotus/utils/escape'
2
+ require 'lotus/presenter'
3
+
4
+ module Lotus
5
+ module View
6
+ # Auto escape logic for views and presenters.
7
+ #
8
+ # @since 0.4.0
9
+ module Escape
10
+ module InstanceMethods
11
+ private
12
+ # Mark the given string as safe to render.
13
+ #
14
+ # !!! ATTENTION !!! This may open your application to XSS attacks.
15
+ #
16
+ # @param string [String] the input string
17
+ #
18
+ # @return [Lotus::Utils::Escape::SafeString] the string marked as safe
19
+ #
20
+ # @since 0.4.0
21
+ # @api public
22
+ #
23
+ # @example View usage
24
+ # require 'lotus/view'
25
+ #
26
+ # User = Struct.new(:name)
27
+ #
28
+ # module Users
29
+ # class Show
30
+ # include Lotus::View
31
+ #
32
+ # def user_name
33
+ # _raw user.name
34
+ # end
35
+ # end
36
+ # end
37
+ #
38
+ # # ERB template
39
+ # # <div id="user_name"><%= user_name %></div>
40
+ #
41
+ # user = User.new("<script>alert('xss')</script>")
42
+ # html = Users::Show.render(format: :html, user: user)
43
+ #
44
+ # html # => <div id="user_name"><script>alert('xss')</script></div>
45
+ #
46
+ # @example Presenter usage
47
+ # require 'lotus/view'
48
+ #
49
+ # User = Struct.new(:name)
50
+ #
51
+ # class UserPresenter
52
+ # include Lotus::Presenter
53
+ #
54
+ # def name
55
+ # _raw @object.name
56
+ # end
57
+ # end
58
+ #
59
+ # user = User.new("<script>alert('xss')</script>")
60
+ # presenter = UserPresenter.new(user)
61
+ #
62
+ # presenter.name # => "<script>alert('xss')</script>"
63
+ def _raw(string)
64
+ ::Lotus::Utils::Escape::SafeString.new(string)
65
+ end
66
+
67
+ # Force the output escape for the given object
68
+ #
69
+ # @param object [Object] the input object
70
+ #
71
+ # @return [Lotus::View::Escape::Presenter] a presenter with output
72
+ # autoescape
73
+ #
74
+ # @since 0.4.0
75
+ # @api public
76
+ #
77
+ # @see Lotus::View::Escape::Presenter
78
+ #
79
+ # @example View usage
80
+ # require 'lotus/view'
81
+ #
82
+ # User = Struct.new(:first_name, :last_name)
83
+ #
84
+ # module Users
85
+ # class Show
86
+ # include Lotus::View
87
+ #
88
+ # def user
89
+ # _escape locals[:user]
90
+ # end
91
+ # end
92
+ # end
93
+ #
94
+ # # ERB template:
95
+ # #
96
+ # # <div id="first_name">
97
+ # # <%= user.first_name %>
98
+ # # </div>
99
+ # # <div id="last_name">
100
+ # # <%= user.last_name %>
101
+ # # </div>
102
+ #
103
+ # first_name = "<script>alert('first_name')</script>"
104
+ # last_name = "<script>alert('last_name')</script>"
105
+ #
106
+ # user = User.new(first_name, last_name)
107
+ # html = Users::Show.render(format: :html, user: user)
108
+ #
109
+ # html
110
+ # # =>
111
+ # # <div id="first_name">
112
+ # # &lt;script&gt;alert(&apos;first_name&apos;)&lt;&#x2F;script&gt;
113
+ # # </div>
114
+ # # <div id="last_name">
115
+ # # &lt;script&gt;alert(&apos;last_name&apos;)&lt;&#x2F;script&gt;
116
+ # # </div>
117
+ def _escape(object)
118
+ ::Lotus::View::Escape::Presenter.new(object)
119
+ end
120
+ end
121
+
122
+ # Auto escape presenter
123
+ #
124
+ # @since 0.4.0
125
+ # @api private
126
+ #
127
+ # @see Lotus::View::Escape::InstanceMethods#_escape
128
+ class Presenter
129
+ include ::Lotus::Presenter
130
+ end
131
+
132
+ # Escape the given input if it's a string, otherwise return the oject as it is.
133
+ #
134
+ # @param input [Object] the input
135
+ #
136
+ # @return [Object,String] the escaped string or the given object
137
+ #
138
+ # @since 0.4.0
139
+ # @api private
140
+ def self.html(input)
141
+ case input
142
+ when String
143
+ Utils::Escape.html(input)
144
+ else
145
+ input
146
+ end
147
+ end
148
+
149
+ # Module extended override
150
+ #
151
+ # @since 0.4.0
152
+ # @api private
153
+ def self.extended(base)
154
+ base.class_eval do
155
+ include ::Lotus::Utils::ClassAttribute
156
+ include ::Lotus::View::Escape::InstanceMethods
157
+
158
+ class_attribute :autoescape_methods
159
+ self.autoescape_methods = {}
160
+ end
161
+ end
162
+
163
+ # Wraps concrete view methods with escape logic.
164
+ #
165
+ # @since 0.4.0
166
+ # @api private
167
+ def method_added(method_name)
168
+ unless autoescape_methods[method_name]
169
+ prepend Module.new {
170
+ module_eval %{
171
+ def #{ method_name }(*args, &blk); ::Lotus::View::Escape.html super; end
172
+ }
173
+ }
174
+
175
+ autoescape_methods[method_name] = true
176
+ end
177
+ end
178
+ end
179
+ end
180
+ end
@@ -21,7 +21,7 @@ module Lotus
21
21
  # @since 0.1.0
22
22
  #
23
23
  # @see Lotus::Layout::ClassMethods#registry
24
- class LayoutRegistry < ::Hash
24
+ class LayoutRegistry
25
25
  # Initialize the registry
26
26
  #
27
27
  # @param view [Class] the view
@@ -29,8 +29,7 @@ module Lotus
29
29
  # @api private
30
30
  # @since 0.1.0
31
31
  def initialize(view)
32
- super()
33
-
32
+ @registry = {}
34
33
  @view = view
35
34
  prepare!
36
35
  end
@@ -50,15 +49,15 @@ module Lotus
50
49
  # @api private
51
50
  # @since 0.1.0
52
51
  def resolve(context)
53
- fetch(format(context)) { NullTemplate.new }
52
+ @registry.fetch(format(context)) { NullTemplate.new }
54
53
  end
55
54
 
56
55
  protected
57
56
  def prepare!
58
57
  templates.each do |template|
59
- merge! template.format => template
58
+ @registry.merge! template.format => template
60
59
  end
61
- self.any? or raise MissingTemplateLayoutError.new(@view)
60
+ @registry.any? or raise MissingTemplateLayoutError.new(@view)
62
61
  end
63
62
 
64
63
  def templates
@@ -33,8 +33,8 @@ module Lotus
33
33
  # @since 0.3.0
34
34
  def inspect
35
35
  base = "#<#{ self.class }:#{'%x' % (self.object_id << 1)}"
36
- base << " @layout=\"#{@layout}\"" if @layout
37
- base << " @scope=\"#{@scope}\"" if @scope
36
+ base << " @layout=\"#{@layout.inspect}\"" if @layout
37
+ base << " @scope=\"#{@scope.inspect}\"" if @scope
38
38
  base << ">"
39
39
  end
40
40
 
@@ -154,11 +154,15 @@ module Lotus
154
154
  # # `article` will be looked up in the view scope first.
155
155
  # # If not found, it will be searched within the layout.
156
156
  def method_missing(m, *args, &blk)
157
- begin
157
+ if @scope.respond_to?(m)
158
158
  @scope.__send__(m, *args, &blk)
159
- rescue
159
+ elsif @layout.respond_to?(m)
160
160
  @layout.__send__(m, *args, &blk)
161
+ else
162
+ super
161
163
  end
164
+ rescue ::NameError
165
+ ::Kernel.raise ::NoMethodError.new("undefined method `#{ m }' for #{ self.inspect }", m)
162
166
  end
163
167
 
164
168
  def renderer(options)
@@ -89,7 +89,7 @@ module Lotus
89
89
  #
90
90
  # @see Lotus::View::Rendering#render
91
91
  def resolve(context)
92
- view, template = fetch(format(context)) { self[DEFAULT_FORMAT] }
92
+ view, template = @registry.fetch(format(context)) { @registry[DEFAULT_FORMAT] }
93
93
  view.new(template, context)
94
94
  end
95
95
 
@@ -101,13 +101,13 @@ module Lotus
101
101
 
102
102
  def prepare_views!
103
103
  views.each do |view|
104
- merge! view.format || DEFAULT_FORMAT => [ view, template_for(view) ]
104
+ @registry.merge! view.format || DEFAULT_FORMAT => [ view, template_for(view) ]
105
105
  end
106
106
  end
107
107
 
108
108
  def prepare_templates!
109
109
  templates.each do |template|
110
- merge! template.format => [ view_for(template), template ]
110
+ @registry.merge! template.format => [ view_for(template), template ]
111
111
  end
112
112
  end
113
113
 
@@ -1,3 +1,4 @@
1
+ require 'lotus/utils/escape'
1
2
  require 'lotus/view/rendering/layout_scope'
2
3
  require 'lotus/view/rendering/template'
3
4
  require 'lotus/view/rendering/partial'
@@ -59,13 +60,15 @@ module Lotus
59
60
 
60
61
  protected
61
62
  def method_missing(m, *args, &block)
62
- if @view.respond_to?(m)
63
- @view.__send__ m, *args, &block
64
- elsif @locals.key?(m)
65
- @locals[m]
66
- else
67
- super
68
- end
63
+ ::Lotus::View::Escape.html(
64
+ if @view.respond_to?(m)
65
+ @view.__send__ m, *args, &block
66
+ elsif @locals.key?(m)
67
+ @locals[m]
68
+ else
69
+ super
70
+ end
71
+ )
69
72
  end
70
73
  end
71
74
  end
@@ -3,6 +3,6 @@ module Lotus
3
3
  # Defines the version
4
4
  #
5
5
  # @since 0.1.0
6
- VERSION = '0.3.0'.freeze
6
+ VERSION = '0.4.0'.freeze
7
7
  end
8
8
  end
data/lotus-view.gemspec CHANGED
@@ -20,7 +20,7 @@ Gem::Specification.new do |spec|
20
20
  spec.required_ruby_version = '>= 2.0.0'
21
21
 
22
22
  spec.add_runtime_dependency 'tilt', '~> 2.0', '>= 2.0.1'
23
- spec.add_runtime_dependency 'lotus-utils', '~> 0.3', '>= 0.3.2'
23
+ spec.add_runtime_dependency 'lotus-utils', '~> 0.4'
24
24
 
25
25
  spec.add_development_dependency 'bundler', '~> 1.5'
26
26
  spec.add_development_dependency 'minitest', '~> 5'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lotus-view
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Luca Guidi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-12-23 00:00:00.000000000 Z
11
+ date: 2015-03-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: tilt
@@ -36,20 +36,14 @@ dependencies:
36
36
  requirements:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
- version: '0.3'
40
- - - ">="
41
- - !ruby/object:Gem::Version
42
- version: 0.3.2
39
+ version: '0.4'
43
40
  type: :runtime
44
41
  prerelease: false
45
42
  version_requirements: !ruby/object:Gem::Requirement
46
43
  requirements:
47
44
  - - "~>"
48
45
  - !ruby/object:Gem::Version
49
- version: '0.3'
50
- - - ">="
51
- - !ruby/object:Gem::Version
52
- version: 0.3.2
46
+ version: '0.4'
53
47
  - !ruby/object:Gem::Dependency
54
48
  name: bundler
55
49
  requirement: !ruby/object:Gem::Requirement
@@ -108,6 +102,7 @@ files:
108
102
  - lib/lotus/view.rb
109
103
  - lib/lotus/view/configuration.rb
110
104
  - lib/lotus/view/dsl.rb
105
+ - lib/lotus/view/escape.rb
111
106
  - lib/lotus/view/inheritable.rb
112
107
  - lib/lotus/view/rendering.rb
113
108
  - lib/lotus/view/rendering/layout_finder.rb
@@ -147,7 +142,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
147
142
  version: '0'
148
143
  requirements: []
149
144
  rubyforge_project:
150
- rubygems_version: 2.2.2
145
+ rubygems_version: 2.4.5
151
146
  signing_key:
152
147
  specification_version: 4
153
148
  summary: View layer for Lotus, with a separation between views and templates