view_models 1.5.2

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