view_models 1.5.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. data/CHANGELOG +26 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.textile +242 -0
  4. data/Rakefile +47 -0
  5. data/VERSION.yml +4 -0
  6. data/generators/view_models/USAGE +6 -0
  7. data/generators/view_models/templates/README +1 -0
  8. data/generators/view_models/templates/spec/view_model_spec.rb +7 -0
  9. data/generators/view_models/templates/view_models/view_model.rb +5 -0
  10. data/generators/view_models/templates/views/_empty.html.haml +0 -0
  11. data/generators/view_models/templates/views/view_models/collection/_collection.html.erb +1 -0
  12. data/generators/view_models/templates/views/view_models/collection/_collection.html.haml +8 -0
  13. data/generators/view_models/templates/views/view_models/collection/_collection.text.erb +6 -0
  14. data/generators/view_models/templates/views/view_models/collection/_list.html.erb +1 -0
  15. data/generators/view_models/templates/views/view_models/collection/_list.html.haml +7 -0
  16. data/generators/view_models/templates/views/view_models/collection/_list.text.erb +6 -0
  17. data/generators/view_models/templates/views/view_models/collection/_pagination.html.haml +12 -0
  18. data/generators/view_models/templates/views/view_models/collection/_pagination.text.erb +3 -0
  19. data/generators/view_models/templates/views/view_models/collection/_table.html.haml +5 -0
  20. data/generators/view_models/templates/views/view_models/collection/_table.text.erb +10 -0
  21. data/generators/view_models/view_models_generator.rb +47 -0
  22. data/lib/extensions/active_record.rb +19 -0
  23. data/lib/extensions/model_reader.rb +115 -0
  24. data/lib/extensions/view.rb +24 -0
  25. data/lib/helpers/collection.rb +124 -0
  26. data/lib/helpers/rails.rb +59 -0
  27. data/lib/helpers/view.rb +22 -0
  28. data/lib/view_models/base.rb +268 -0
  29. data/lib/view_models/controller_extractor.rb +24 -0
  30. data/lib/view_models/path_store.rb +61 -0
  31. data/lib/view_models/render_options.rb +109 -0
  32. data/lib/view_models/view.rb +26 -0
  33. data/lib/view_models.rb +3 -0
  34. data/rails/init.rb +18 -0
  35. data/spec/integration/integration_spec.rb +269 -0
  36. data/spec/integration/models/sub_subclass.rb +14 -0
  37. data/spec/integration/models/subclass.rb +3 -0
  38. data/spec/integration/view_models/project.rb +14 -0
  39. data/spec/integration/view_models/sub_subclass.rb +42 -0
  40. data/spec/integration/view_models/subclass.rb +1 -0
  41. data/spec/integration/views/view_models/collection/_collection.html.erb +1 -0
  42. data/spec/integration/views/view_models/collection/_collection.text.erb +6 -0
  43. data/spec/integration/views/view_models/collection/_list.html.erb +1 -0
  44. data/spec/integration/views/view_models/collection/_list.text.erb +6 -0
  45. data/spec/integration/views/view_models/sub_subclass/_capture_in_template.erb +2 -0
  46. data/spec/integration/views/view_models/sub_subclass/_capture_in_view_model.erb +3 -0
  47. data/spec/integration/views/view_models/sub_subclass/_collection_example.html.erb +3 -0
  48. data/spec/integration/views/view_models/sub_subclass/_collection_example.text.erb +3 -0
  49. data/spec/integration/views/view_models/sub_subclass/_collection_item.html.erb +1 -0
  50. data/spec/integration/views/view_models/sub_subclass/_collection_item.text.erb +1 -0
  51. data/spec/integration/views/view_models/sub_subclass/_exists.erb +1 -0
  52. data/spec/integration/views/view_models/sub_subclass/_exists.html.erb +1 -0
  53. data/spec/integration/views/view_models/sub_subclass/_exists.text.erb +1 -0
  54. data/spec/integration/views/view_models/sub_subclass/_exists_in_both.erb +1 -0
  55. data/spec/integration/views/view_models/sub_subclass/_inner.also_explicit.erb +1 -0
  56. data/spec/integration/views/view_models/sub_subclass/_inner.nesting.erb +1 -0
  57. data/spec/integration/views/view_models/sub_subclass/_list_example.html.erb +3 -0
  58. data/spec/integration/views/view_models/sub_subclass/_list_example.text.erb +3 -0
  59. data/spec/integration/views/view_models/sub_subclass/_list_item.html.erb +1 -0
  60. data/spec/integration/views/view_models/sub_subclass/_list_item.text.erb +1 -0
  61. data/spec/integration/views/view_models/sub_subclass/_outer.explicit.erb +1 -0
  62. data/spec/integration/views/view_models/sub_subclass/_outer.nesting.erb +1 -0
  63. data/spec/integration/views/view_models/sub_subclass/_part_that_is_dependent_on_the_view_model.erb +1 -0
  64. data/spec/integration/views/view_models/sub_subclass/show.html.erb +1 -0
  65. data/spec/integration/views/view_models/sub_subclass/show.text.erb +1 -0
  66. data/spec/integration/views/view_models/subclass/_exists_in_both.erb +1 -0
  67. data/spec/integration/views/view_models/subclass/_no_sub_subclass.erb +1 -0
  68. data/spec/integration/views/view_models/subclass/_not_found_in_sub_subclass.erb +1 -0
  69. data/spec/lib/extensions/active_record_spec.rb +31 -0
  70. data/spec/lib/extensions/model_reader_spec.rb +93 -0
  71. data/spec/lib/helpers/collection_spec.rb +196 -0
  72. data/spec/lib/helpers/rails_spec.rb +88 -0
  73. data/spec/lib/helpers/view_spec.rb +20 -0
  74. data/spec/lib/view_models/base_spec.rb +102 -0
  75. data/spec/lib/view_models/view_spec.rb +9 -0
  76. data/spec/spec_helper.rb +14 -0
  77. data/spec/spec_helper_extensions.rb +13 -0
  78. metadata +156 -0
data/rails/init.rb ADDED
@@ -0,0 +1,18 @@
1
+ require 'extensions/active_record'
2
+ require 'extensions/model_reader'
3
+
4
+ require 'view_models'
5
+ require 'view_models/render_options'
6
+ require 'view_models/controller_extractor'
7
+ require 'view_models/path_store'
8
+ require 'view_models/base'
9
+ require 'view_models/view'
10
+
11
+ require 'helpers/view'
12
+ require 'helpers/rails'
13
+ require 'helpers/collection'
14
+
15
+ #
16
+ # ActionController::Base.send :helper, ViewModels::Helpers::Rails
17
+ ActionController::Base.send :include, ViewModels::Helpers::Rails
18
+ ActionView::Base.send :include, ViewModels::Helpers::Rails
@@ -0,0 +1,269 @@
1
+ require File.join(File.dirname(__FILE__), '../spec_helper')
2
+
3
+ require File.join(File.dirname(__FILE__), 'models/subclass')
4
+ require File.join(File.dirname(__FILE__), 'models/sub_subclass')
5
+
6
+ require 'helpers/rails'
7
+ ActionView::Base.send :include, ViewModels::Helpers::Rails
8
+
9
+ require 'view_models/base'
10
+ require File.join(File.dirname(__FILE__), 'view_models/project')
11
+ require File.join(File.dirname(__FILE__), 'view_models/subclass')
12
+ require File.join(File.dirname(__FILE__), 'view_models/sub_subclass')
13
+
14
+ require 'action_controller'
15
+ require 'action_controller/test_process'
16
+
17
+ class TestController < ActionController::Base; end
18
+
19
+ describe 'Integration' do
20
+
21
+ before(:each) do
22
+ begin
23
+ @controller = TestController.new
24
+ @controller.class.view_paths = ['spec/integration/views']
25
+
26
+ @logger = stub :logger, :null_object => true
27
+ @controller.logger = @logger
28
+
29
+ @request = ActionController::TestRequest.new
30
+ @response = ActionController::TestResponse.new
31
+ @controller.request = @request
32
+ @controller.response = @response
33
+
34
+ # TODO Make separate contexts, one where the controller has rendered, one where it has not.
35
+ #
36
+ # Let the Controller generate a view instance.
37
+ #
38
+ # @controller.process @request, @response
39
+
40
+ @view = ActionView::Base.new @controller.class.view_paths, {}, @controller
41
+ @model = SubSubclass.new
42
+ @model.id = :some_id
43
+ @view_model = ViewModels::SubSubclass.new @model, @view
44
+ # rescue Exception => e
45
+ # puts e.message
46
+ # puts e.backtrace
47
+ end
48
+ end
49
+
50
+ before(:all) { puts "\n#{self.send(:description_args)[0]}:" }
51
+
52
+ describe 'ActiveRecord Extensions' do
53
+ before(:each) do
54
+ @view_model.extend ViewModels::Extensions::ActiveRecord
55
+ end
56
+ it 'should delegate the id' do
57
+ @view_model.id.should == :some_id
58
+ end
59
+ it 'should delegate the id' do
60
+ @view_model.dom_id.should == 'sub_subclass_some_id'
61
+ end
62
+ end
63
+
64
+ describe 'view_model_for' do
65
+ context 'view model' do
66
+ it 'should be available' do
67
+ lambda { @view_model.view_model_for @model }.should_not raise_error
68
+ end
69
+ it 'should return the right one' do
70
+ @view_model.view_model_for(@model).should be_kind_of(ViewModels::SubSubclass)
71
+ end
72
+ end
73
+ end
74
+ describe 'view_model_class_for' do
75
+ context 'view model' do
76
+ it 'should be available' do
77
+ lambda { @view_model.view_model_class_for @model }.should_not raise_error
78
+ end
79
+ it 'should return the right class' do
80
+ @view_model.view_model_class_for(@model).should == ViewModels::SubSubclass
81
+ end
82
+ end
83
+ end
84
+
85
+ describe 'capture in view model method' do
86
+ context 'template' do
87
+ it 'should capture the content of the block' do
88
+ @view_model.render_as(:capture_in_template).should == 'Capturing: A Pirate!'
89
+ end
90
+ end
91
+ context 'in view model' do
92
+ it 'should capture the content of the block' do
93
+ @view_model.render_as(:capture_in_view_model).should == 'Capturing: A Pirate!'
94
+ end
95
+ end
96
+ end
97
+
98
+ describe 'controller context' do
99
+ it 'should work' do
100
+ controller = ActionController::Base.new
101
+
102
+ lambda {
103
+ ViewModels::SubSubclass.new @model, controller
104
+ }.should_not raise_error
105
+ end
106
+ end
107
+
108
+ describe 'view_model_for inclusion in view' do
109
+ it 'should be included' do
110
+ view = ActionView::Base.new
111
+
112
+ lambda {
113
+ view.view_model_for @model
114
+ }.should_not raise_error
115
+ end
116
+ end
117
+
118
+ describe 'collection rendering' do
119
+ context 'default format' do
120
+ it 'should render a html list' do
121
+ @view_model.render_as(:list_example).should == "<ol class=\"collection\"><li>_list_item.html.erb</li><li>_list_item.html.erb</li></ol>"
122
+ end
123
+ it 'should render a html collection' do
124
+ @view_model.render_as(:collection_example).should == "<ul class=\"collection\"><li>_collection_item.html.erb</li><li>_collection_item.html.erb</li></ul>"
125
+ end
126
+ end
127
+ context 'format html' do
128
+ it 'should render a html list' do
129
+ @view_model.render_as(:list_example, :format => :html).should == "<ol class=\"collection\"><li>_list_item.html.erb</li><li>_list_item.html.erb</li></ol>"
130
+ end
131
+ it 'should render a html collection' do
132
+ @view_model.render_as(:collection_example, :format => :html).should == "<ul class=\"collection\"><li>_collection_item.html.erb</li><li>_collection_item.html.erb</li></ul>"
133
+ end
134
+ end
135
+ context 'format text' do
136
+ it 'should render a text list' do
137
+ @view_model.render_as(:list_example, :format => :text).should == '_list_item.text.erb\n_list_item.text.erb'
138
+ end
139
+ it 'should render a text collection' do
140
+ @view_model.render_as(:collection_example, :format => :text).should == '_collection_item.text.erb_collection_item.text.erb'
141
+ end
142
+ end
143
+ end
144
+
145
+ describe 'model attributes' do
146
+ it 'should pass through unfiltered attributes' do
147
+ @view_model.some_untouched_attribute.should == :some_value
148
+ end
149
+ it 'should filter some attributes' do
150
+ @view_model.some_filtered_attribute.should == '%3Cscript%3Efilter+me%3C%2Fscript%3E'
151
+ end
152
+ it 'should filter some attributes multiple times' do
153
+ @view_model.some_doubly_doubled_attribute.should == 'blahblahblahblah'
154
+ end
155
+ it 'should filter some attributes multiple times correctly' do
156
+ @view_model.some_mangled_attribute.should == 'DCBDCB'
157
+ end
158
+ end
159
+
160
+ describe 'controller_method' do
161
+ it 'should delegate to the context' do
162
+ @view_model.logger.should == @logger
163
+ end
164
+ end
165
+
166
+ describe "render_template" do
167
+ it "should render the right template" do
168
+ @view_model.render_template(:show).should == 'show.html.erb'
169
+ end
170
+ it "should render the right template with format" do
171
+ @view_model.render_template(:show, :format => :text).should == 'show.text.erb'
172
+ end
173
+ end
174
+
175
+ describe 'render_as' do
176
+ describe 'render_the alias' do
177
+ it 'should also render' do
178
+ @view_model.render_the(:part_that_is_dependent_on_the_view_model).should == '_part_that_is_dependent_on_the_view_model.erb'
179
+ end
180
+ end
181
+ describe "explicit partial rendering" do
182
+ it "should render the right partial" do
183
+ # If one wants explicit template rendering, he needs to work more.
184
+ # Let's be opinionated here :)
185
+ #
186
+ @view_model.render_as(:partial => 'view_models/sub_subclass/inner', :format => :nesting).should == '_inner.nesting.erb'
187
+ end
188
+ end
189
+ describe "nesting" do
190
+ it "should render the right nested template, with an explicitly defined format (see template)" do
191
+ @view_model.render_as(:outer, :format => :explicit).should == '_inner.also_explicit.erb'
192
+ end
193
+ it "should render the right nested template, respecting the already defined format" do
194
+ @view_model.render_as(:outer, :format => :nesting).should == '_inner.nesting.erb'
195
+ end
196
+ end
197
+ describe 'template inheritance' do
198
+ it 'should raise ViewModels::MissingTemplateError if template is not found' do
199
+ lambda { @view_model.render_as(:this_template_does_not_exist_at_allllll) }.should raise_error(ViewModels::MissingTemplateError, "No template '_this_template_does_not_exist_at_allllll' with default format found.")
200
+ end
201
+ it 'should raise ViewModels::MissingTemplateError if template is not found, with specific path' do
202
+ lambda { @view_model.render_as(:partial => 'view_models/sub_subclass/this_template_does_not_exist_at_allllll') }.should raise_error(ViewModels::MissingTemplateError, "No template 'view_models/sub_subclass/_this_template_does_not_exist_at_allllll' with default format found.")
203
+ end
204
+ it 'should raise ViewModels::MissingTemplateError if template is not found, with format' do
205
+ lambda { @view_model.render_as(:this_template_does_not_exist_at_allllll, :format => :gaga) }.should raise_error(ViewModels::MissingTemplateError, "No template '_this_template_does_not_exist_at_allllll' with format gaga found.")
206
+ end
207
+ it "should use its own template" do
208
+ @view_model.render_as(:exists).should == '_exists.html.erb' # The default
209
+ end
210
+ it "should use the subclass' template" do
211
+ @view_model.render_as(:no_sub_subclass).should == '_no_sub_subclass.erb'
212
+ end
213
+ end
214
+ describe 'format' do
215
+ it 'should render html' do
216
+ @view_model.render_as(:exists, :format => nil).should == '_exists.html.erb' # the default
217
+ end
218
+ it 'should render erb' do
219
+ @view_model.render_as(:exists, :format => '').should == '_exists.erb'
220
+ end
221
+ it 'should render text' do
222
+ @view_model.render_as(:exists, :format => :text).should == '_exists.text.erb'
223
+ end
224
+ it 'should render html' do
225
+ @view_model.render_as(:exists, :format => :html).should == '_exists.html.erb'
226
+ end
227
+ end
228
+ describe 'locals' do
229
+ it 'should render html' do
230
+ @view_model.render_as(:exists, :locals => { :local_name => :some_local }).should == '_exists.html.erb with some_local'
231
+ end
232
+ it 'should render html' do
233
+ @view_model.render_as(:exists, :format => nil, :locals => { :local_name => :some_local }).should == '_exists.html.erb with some_local'
234
+ end
235
+ it 'should render text' do
236
+ @view_model.render_as(:exists, :format => '', :locals => { :local_name => :some_local }).should == '_exists.erb with some_local'
237
+ end
238
+ it 'should render text' do
239
+ @view_model.render_as(:exists, :format => :text, :locals => { :local_name => :some_local }).should == '_exists.text.erb with some_local'
240
+ end
241
+ it 'should render html' do
242
+ @view_model.render_as(:exists, :format => :html, :locals => { :local_name => :some_local }).should == '_exists.html.erb with some_local'
243
+ end
244
+ end
245
+ describe 'memoizing' do
246
+ it 'should memoize and not generate always a new path' do
247
+ @view_model.class.should_receive(:generate_template_path_from).once
248
+
249
+ @view_model.render_as :not_found_in_sub_subclass
250
+ @view_model.render_as :not_found_in_sub_subclass
251
+ @view_model.render_as :not_found_in_sub_subclass
252
+ @view_model.render_as :not_found_in_sub_subclass
253
+ @view_model.render_as :not_found_in_sub_subclass
254
+ end
255
+ it 'should render the right one' do
256
+ @view_model.render_as :exists_in_both
257
+ @view_model.render_as(:exists_in_both).should == 'in sub subclass'
258
+
259
+ other_view_model = ViewModels::Subclass.new Subclass.new, @view
260
+ other_view_model.render_as :exists_in_both
261
+ other_view_model.render_as(:exists_in_both).should == 'in subclass'
262
+
263
+ @view_model.render_as :exists_in_both
264
+ @view_model.render_as(:exists_in_both).should == 'in sub subclass'
265
+ end
266
+ end
267
+ end
268
+
269
+ end
@@ -0,0 +1,14 @@
1
+ class SubSubclass
2
+
3
+ attr_accessor :id
4
+
5
+ attr_reader :some_untouched_attribute, :some_filtered_attribute, :some_doubly_doubled_attribute, :some_mangled_attribute
6
+
7
+ def initialize
8
+ @some_untouched_attribute = :some_value
9
+ @some_filtered_attribute = '<script>filter me</script>'
10
+ @some_doubly_doubled_attribute = 'blah'
11
+ @some_mangled_attribute = 'abcd'
12
+ end
13
+
14
+ end
@@ -0,0 +1,3 @@
1
+ class Subclass
2
+
3
+ end
@@ -0,0 +1,14 @@
1
+ class ViewModels::Project < ViewModels::Base
2
+
3
+ helper :all
4
+
5
+ # helper ActionView::Helpers::CaptureHelper
6
+
7
+ helper ViewModels::Helpers::Rails
8
+ helper ViewModels::Helpers::View
9
+
10
+ # def output_buffer= b
11
+ # view.output_buffer = b
12
+ # end
13
+
14
+ end
@@ -0,0 +1,42 @@
1
+ class ViewModels::SubSubclass < ViewModels::Subclass
2
+
3
+ controller_method :logger
4
+
5
+ model_reader :some_untouched_attribute
6
+ model_reader :some_filtered_attribute, :filter_through => :h
7
+ model_reader :some_doubly_doubled_attribute, :filter_through => [:doubled]*2
8
+ model_reader :some_mangled_attribute, :filter_through => [:reverse, :cutoff, :doubled, :upcase]
9
+
10
+ # h as we know it.
11
+ #
12
+ def h v
13
+ CGI.escape v
14
+ end
15
+
16
+ # Multiplies the text by 2.
17
+ #
18
+ def doubled text
19
+ text*2
20
+ end
21
+
22
+ # Cuts off the last 2 characters. Or all, if less than size 2.
23
+ #
24
+ def cutoff text
25
+ text[0..-2]
26
+ end
27
+
28
+ def reverse text
29
+ text.reverse
30
+ end
31
+
32
+ def upcase text
33
+ text.upcase
34
+ end
35
+
36
+ # Captures the block as a string.
37
+ #
38
+ def capture_block &block
39
+ capture &block
40
+ end
41
+
42
+ end
@@ -0,0 +1 @@
1
+ class ViewModels::Subclass < ViewModels::Project; end
@@ -0,0 +1 @@
1
+ <ul class="collection"><%- collection.each do |item| -%><%- view_model = view_model_for item -%><li><%- options = template_format ? { :format => template_format } : {} -%><%= view_model.render_as template_name, options -%></li><%= separator unless item == collection.last %><%- end -%></ul>
@@ -0,0 +1,6 @@
1
+ <%- collection.each do |item| -%>
2
+ <%- view_model = view_model_for item -%>
3
+ <%- options = (template_format ? { :format => template_format } : {}) -%>
4
+ <%= view_model.render_as template_name, options -%>
5
+ <%= separator unless item == collection.entries.last -%>
6
+ <%- end -%>
@@ -0,0 +1 @@
1
+ <ol class="collection"><%- collection.each do |item| -%><%- view_model = view_model_for item -%><li><%- options = (template_format ? { :format => template_format } : {}) -%><%= view_model.render_as template_name, options -%></li><%= separator unless item == collection.last %><%- end -%></ol>
@@ -0,0 +1,6 @@
1
+ <% collection.each do |item| -%>
2
+ <% view_model = view_model_for item -%>
3
+ <%- options = (template_format ? { :format => template_format } : {}) -%>
4
+ <%= view_model.render_as template_name, options -%>
5
+ <%= separator || '\n' unless item == collection.entries.last -%>
6
+ <% end -%>
@@ -0,0 +1,2 @@
1
+ <%- @actor = capture do -%>A Pirate!<% end -%>
2
+ Capturing: <%= @actor -%>
@@ -0,0 +1,3 @@
1
+ Capturing: <%- view_model.capture_block do -%>
2
+ <%= "A Pirate!" -%>
3
+ <%- end -%>
@@ -0,0 +1,3 @@
1
+ <%- ary = [SubSubclass.new, SubSubclass.new] -%>
2
+ <%- cvm = collection_view_model_for ary -%>
3
+ <%= cvm.collection -%>
@@ -0,0 +1,3 @@
1
+ <%- ary = [SubSubclass.new, SubSubclass.new] -%>
2
+ <%- cvm = collection_view_model_for ary -%>
3
+ <%= cvm.collection -%>
@@ -0,0 +1 @@
1
+ _collection_item.html.erb
@@ -0,0 +1 @@
1
+ _collection_item.text.erb
@@ -0,0 +1 @@
1
+ _exists.erb<%= " with #{local_name}" if defined?(local_name) %>
@@ -0,0 +1 @@
1
+ _exists.html.erb<%= " with #{local_name}" if defined?(local_name) %>
@@ -0,0 +1 @@
1
+ _exists.text.erb<%= " with #{local_name}" if defined?(local_name) %>
@@ -0,0 +1 @@
1
+ _inner.also_explicit.erb
@@ -0,0 +1,3 @@
1
+ <%- ary = [SubSubclass.new, SubSubclass.new] -%>
2
+ <%- cvm = collection_view_model_for ary -%>
3
+ <%= cvm.list -%>
@@ -0,0 +1,3 @@
1
+ <%- ary = [SubSubclass.new, SubSubclass.new] -%>
2
+ <%- cvm = collection_view_model_for ary -%>
3
+ <%= cvm.list -%>
@@ -0,0 +1 @@
1
+ _list_item.html.erb
@@ -0,0 +1 @@
1
+ _list_item.text.erb
@@ -0,0 +1 @@
1
+ <%= view_model.render_as(:inner, :format => :also_explicit) -%>
@@ -0,0 +1 @@
1
+ <%= view_model.render_as :inner -%>
@@ -0,0 +1 @@
1
+ _part_that_is_dependent_on_the_view_model.erb
@@ -0,0 +1 @@
1
+ _no_sub_subclass.erb
@@ -0,0 +1 @@
1
+ _not_found_in_sub_subclass.erb
@@ -0,0 +1,31 @@
1
+ require File.join(File.dirname(__FILE__), '../../spec_helper')
2
+
3
+ describe ViewModels::Extensions::ActiveRecord do
4
+
5
+ describe "to_param" do
6
+ before(:each) do
7
+ @model = stub :model
8
+ @view_model = ViewModels::Base.new @model, nil
9
+ @view_model.extend ViewModels::Extensions::ActiveRecord
10
+ end
11
+ it "should delegate to_param to the model" do
12
+ @model.should_receive(:to_param).once
13
+
14
+ @view_model.to_param
15
+ end
16
+
17
+ it "should delegate id to the model" do
18
+ @model.should_receive(:id).once
19
+
20
+ @view_model.id
21
+ end
22
+
23
+ it "should delegate dom_id to ActionController::RecordIdentifier" do
24
+ ActionController::RecordIdentifier.should_receive(:dom_id).once
25
+
26
+ @view_model.dom_id
27
+ end
28
+
29
+ end
30
+
31
+ end
@@ -0,0 +1,93 @@
1
+ require File.join(File.dirname(__FILE__), '../../spec_helper')
2
+
3
+ require 'extensions/model_reader'
4
+
5
+ describe ViewModels::Extensions::ModelReader do
6
+
7
+ class ModelReaderModel < Struct.new(:some_model_value); end
8
+
9
+ describe ".model_reader" do
10
+ before(:each) do
11
+ @model = ModelReaderModel.new
12
+ @view_model = ViewModels::Base.new(@model, nil)
13
+ class << @view_model
14
+ def a(s); s << 'a' end
15
+ def b(s); s << 'b' end
16
+ end
17
+ @model.some_model_value = 's'
18
+ end
19
+ it "should call filters in a given pattern" do
20
+ @view_model.class.model_reader :some_model_value, :filter_through => [:a, :b, :a, :a]
21
+
22
+ @view_model.some_model_value.should == 'sabaa'
23
+ end
24
+ it "should pass through the model value if no filters are installed" do
25
+ @view_model.class.model_reader :some_model_value
26
+
27
+ @view_model.some_model_value.should == 's'
28
+ end
29
+ it "should call filters in a given pattern" do
30
+ @view_model.class.model_reader [:some_model_value], :filter_through => [:a, :b, :a, :a]
31
+
32
+ @view_model.some_model_value.should == 'sabaa'
33
+ end
34
+ it "should handle a single filter" do
35
+ @view_model.class.model_reader :some_model_value, :filter_through => :a
36
+
37
+ lambda { @view_model.some_model_value }.should_not raise_error
38
+ end
39
+ it "should handle an array of readers" do
40
+ @view_model.class.model_reader [:some_model_value, :some_other_model_value], :filter_through => :a
41
+
42
+ lambda { @view_model.some_model_value }.should_not raise_error
43
+ end
44
+ end
45
+
46
+ describe 'FilteredDelegationInstaller' do
47
+ before(:each) do
48
+ @model = ModelReaderModel.new
49
+ end
50
+ context 'with filters' do
51
+ before(:each) do
52
+ @options = ViewModels::Extensions::ModelReader::Options.new :some_attribute_name, :filter_through => [:filter1, :filter2]
53
+ @installer = ViewModels::Extensions::ModelReader::FilteredDelegationInstaller.new @model, @options
54
+ end
55
+ it 'should have a correct filter definition' do
56
+ @installer.reader_definition_for(:some_attribute).should == 'def some_attribute; filter2(filter1(model.some_attribute)); end'
57
+ end
58
+ end
59
+ context 'without filters' do
60
+ before(:each) do
61
+ @options = ViewModels::Extensions::ModelReader::Options.new :some_attribute_name
62
+ @installer = ViewModels::Extensions::ModelReader::FilteredDelegationInstaller.new @model, @options
63
+ end
64
+ it 'should have a correct filter definition' do
65
+ @installer.reader_definition_for(:some_attribute).should == 'def some_attribute; model.some_attribute; end'
66
+ end
67
+ end
68
+ end
69
+
70
+ describe 'Options' do
71
+ context 'without filters' do
72
+ before(:each) do
73
+ @options = ViewModels::Extensions::ModelReader::Options.new :some_attribute_name
74
+ end
75
+ describe 'split' do
76
+ it 'should return the right values' do
77
+ @options.to_a.should == [[:some_attribute_name], []]
78
+ end
79
+ end
80
+ end
81
+ context 'with filters' do
82
+ before(:each) do
83
+ @options = ViewModels::Extensions::ModelReader::Options.new :some_attribute_name, :filter_through => [:filter1, :filter2]
84
+ end
85
+ describe 'split' do
86
+ it 'should return the right values, with flipped options' do
87
+ @options.to_a.should == [[:some_attribute_name], [:filter2, :filter1]]
88
+ end
89
+ end
90
+ end
91
+ end
92
+
93
+ end