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 +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
|