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 +4 -4
- data/CHANGELOG.md +9 -0
- data/README.md +170 -1
- data/lib/lotus/layout.rb +2 -0
- data/lib/lotus/presenter.rb +46 -2
- data/lib/lotus/view.rb +2 -0
- data/lib/lotus/view/dsl.rb +21 -1
- data/lib/lotus/view/escape.rb +180 -0
- data/lib/lotus/view/rendering/layout_registry.rb +5 -6
- data/lib/lotus/view/rendering/layout_scope.rb +8 -4
- data/lib/lotus/view/rendering/registry.rb +3 -3
- data/lib/lotus/view/rendering/scope.rb +10 -7
- data/lib/lotus/view/version.rb +1 -1
- data/lotus-view.gemspec +1 -1
- metadata +6 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 500bbc3ad33d7a944aa35980d6257df6c9653c01
|
4
|
+
data.tar.gz: 319d161fcdf16cbe089df0e178d8b07b5fd9ee09
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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 # => "<script>alert('xss')</script>"
|
663
|
+
|
664
|
+
# THIS IS THE RENDERING OUTPUT:
|
665
|
+
Users::Show.render(format: :html, user: user)
|
666
|
+
# => <div id="user_name"><script>alert('xss')</script></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 # => "<script>alert('xss')</script>"
|
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
|
+
# <script>alert('first_name')</script>
|
735
|
+
# </div>
|
736
|
+
# <div id="last_name">
|
737
|
+
# <script>alert('last_name')</script>
|
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
data/lib/lotus/presenter.rb
CHANGED
@@ -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
|
+
# # => "<script>alert('xss')</script>"
|
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__
|
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
|
data/lib/lotus/view/dsl.rb
CHANGED
@@ -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
|
+
# # <script>alert('first_name')</script>
|
113
|
+
# # </div>
|
114
|
+
# # <div id="last_name">
|
115
|
+
# # <script>alert('last_name')</script>
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
157
|
+
if @scope.respond_to?(m)
|
158
158
|
@scope.__send__(m, *args, &blk)
|
159
|
-
|
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)) {
|
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
|
-
|
63
|
-
@view.
|
64
|
-
|
65
|
-
@locals
|
66
|
-
|
67
|
-
|
68
|
-
|
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
|
data/lib/lotus/view/version.rb
CHANGED
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.
|
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.
|
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:
|
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.
|
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.
|
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.
|
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
|