lotus-view 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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