view_models 1.5.6 → 1.5.7
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.
- data/README.textile +20 -234
- data/TODO.textile +3 -2
- data/VERSION.yml +1 -1
- data/lib/experimental/modules_in_render_hierarchy.rb +3 -3
- data/lib/view_models/render_options.rb +22 -14
- data/spec/integration/integration_spec.rb +1 -1
- data/spec/integration/view_models/module_for_rendering.rb +2 -2
- data/spec/lib/helpers/rails_spec.rb +0 -4
- metadata +3 -3
data/README.textile
CHANGED
@@ -1,22 +1,16 @@
|
|
1
|
-
h1. ViewModels
|
1
|
+
h1. ViewModels
|
2
2
|
|
3
|
-
|
3
|
+
A view model/representer solution for Rails.
|
4
4
|
|
5
|
-
|
5
|
+
h2. Features
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
"Severin":http://github.com/severin @http://github.com/severin@, for bits and pieces here and there.
|
16
|
-
|
17
|
-
"rainhead":http://github.com/rainhead @http://github.com/rainhead@, for the idea to subclass ActionView::Base.
|
18
|
-
|
19
|
-
Important note: These guys rock! :)
|
7
|
+
* Polymorph view model objects that correspond to model objects.
|
8
|
+
* View model specific templates.
|
9
|
+
* Hierarchical Template Rendering: Allows generalized templates for a class tree of view models.
|
10
|
+
* Helper methods directly on the view models.
|
11
|
+
* No view related code in the models.
|
12
|
+
* A clean API for use in Rails.
|
13
|
+
* 100% rcov coverage, nice metrics, gallons of blood and sweat by excellent contributors.
|
20
14
|
|
21
15
|
h2. Installation
|
22
16
|
|
@@ -24,7 +18,7 @@ h3. Rails Plugin
|
|
24
18
|
|
25
19
|
@script/plugin install git://github.com/floere/view_models.git@
|
26
20
|
|
27
|
-
h3. Gem for use in Rails
|
21
|
+
h3. Gem (for use in Rails)
|
28
22
|
|
29
23
|
@gem install view_models@
|
30
24
|
|
@@ -34,221 +28,13 @@ and then adding the line
|
|
34
28
|
|
35
29
|
in your environment.rb.
|
36
30
|
|
37
|
-
h2.
|
38
|
-
|
39
|
-
A possible view_model solution, i.e. no view logic in model code.
|
40
|
-
|
41
|
-
h2. Feedback
|
42
|
-
|
43
|
-
Ask/Write florian.hanke@gmail.com if you have questions/feedback, thanks! :)
|
44
|
-
Fork if you have improvements. Send me a pull request, it is much appreciated.
|
45
|
-
|
46
|
-
h2. Problem
|
47
|
-
|
48
|
-
Display Methods are not well placed either in
|
49
|
-
* models: Violation of the MVC principle.
|
50
|
-
* helpers: No Polymorphism.
|
51
|
-
|
52
|
-
h2. Solution
|
53
|
-
|
54
|
-
A thin proxy layer over a model, with access to the controller, used by the view or controller.
|
55
|
-
|
56
|
-
@The view@ -> to @the view model@ which in turn -> @the model@ and -> @the controller@
|
57
|
-
|
58
|
-
h2. Examples & What you can do
|
59
|
-
|
60
|
-
h3. A quick one
|
61
|
-
|
62
|
-
In the view:
|
63
|
-
<pre><code>user = view_model_for @user
|
64
|
-
%h1= "You, #{user.full_name}, the user"
|
65
|
-
%h2 This is how you look as a search result:
|
66
|
-
= user.render_as :result
|
67
|
-
%h2 This is how you look as a Vcard:
|
68
|
-
= user.render_as :vcard</code></pre>
|
69
|
-
In the view model:
|
70
|
-
<pre><code>class ViewModels::User < ViewModels::Project
|
71
|
-
model_reader :first_name, :last_name
|
72
|
-
def full_name
|
73
|
-
"#{last_name}, #{first_name}"
|
74
|
-
end
|
75
|
-
end</code></pre>
|
76
|
-
In the model:
|
77
|
-
<pre><code>class User < ActiveRecord::Base
|
78
|
-
end</code></pre>
|
79
|
-
Also, there are two partials in @app/views/view_models/user/@, @_result.html.haml@ and @_vcard.html.haml@ that define how the result of @user.render_as :result@ and @user.render_as :vcard@ look. The ViewModel can be accessed inside the view by using the local variable @view_model@.
|
80
|
-
|
81
|
-
h3. Getting a view_model in a view or a controller.
|
82
|
-
|
83
|
-
Call view_model_for: @view_model_instance = view_model_for model_instance@
|
84
|
-
By convention, uses @ViewModels::Model::Class::Name@, thus prefixing @ViewModels::@ to the model class name.
|
85
|
-
|
86
|
-
Note: You can override @specific_view_model_class_for@ to change the mapping of model to class, or make it dynamic.
|
87
|
-
|
88
|
-
h3. Getting a collection view model in a view.
|
89
|
-
|
90
|
-
The collection view_model renders each of the given items with its view_model.
|
91
|
-
|
92
|
-
Call collection_view_model_for: @collection_view_model_instance = collection_view_model_for enumerable_containing_model_instances@
|
93
|
-
|
94
|
-
Rendering a list: @collection_view_model_instance.list@
|
95
|
-
|
96
|
-
Rendering a collection: @collection_view_model_instance.collection@
|
97
|
-
|
98
|
-
Rendering a table: @collection_view_model_instance.table@
|
99
|
-
|
100
|
-
Rendering a pagination: @collection_view_model_instance.pagination@
|
101
|
-
Note: Only works if the passed parameter for @collection_view_model_for@ is a @PaginationEnumeration@.
|
102
|
-
|
103
|
-
Important note:
|
104
|
-
As of yet it is needed to copy the templates/views/view_models/collection
|
105
|
-
directory to the corresponding location in app/views/view_models/collection.
|
106
|
-
This is only needed if you wish to use the collection view model.
|
107
|
-
The collections are automatically copied if you use the generator.
|
108
|
-
|
109
|
-
Note: Rewrite the collection templates as needed, they are rather basic.
|
110
|
-
|
111
|
-
h3. Writing filtered delegate methods on the view model.
|
112
|
-
|
113
|
-
Will create two delegate methods first_name and last_name that delegate to the model: @model_reader :first_name, :last_name@
|
114
|
-
|
115
|
-
Will create a description delegate method that filters the model value through h: @model_reader :description, :filter_through => :h@
|
116
|
-
|
117
|
-
Will create a description delegate method that filters the model value through first textilize, then h: @model_reader :description, :filter_through => [:textilize, :h]@
|
118
|
-
|
119
|
-
Will create both a first_name and last_name delegate method that filters the model value through first textilize, then h: @model_reader :first_name, :last_name, :filter_through => [:textilize, :h]@
|
120
|
-
Note: Filter methods can be any method on the view_model with arity 1.
|
121
|
-
|
122
|
-
h3. Rendering view model templates
|
123
|
-
|
124
|
-
Use @render_as(template_name, options)@.
|
125
|
-
|
126
|
-
Gets a @ViewModels::Model::Class@ instance: @view_model = view_model_for Model::Class.new@
|
127
|
-
|
128
|
-
Gets a @ViewModels::<model_instance.class.name>@ instance: @view_model = view_model_for model_instance@
|
129
|
-
|
130
|
-
Renders the 'example' partial in view_models/model/class: @view_model.render_as :example@
|
131
|
-
Note: Renders a format depending on the request. ../index.text will render example.text.erb.
|
132
|
-
|
133
|
-
Renders the 'example.text.erb' partial in view_models/model/class: @view_model.render_as :example, :format => :text@
|
134
|
-
Note: If the partial cannot be found, it will traverse the view model hierarchy upwards to find a partial template.
|
135
|
-
|
136
|
-
Locals can be passed through as usual: @view_model.render_as :example, :format => :text, :locals => { :name => value }@
|
137
|
-
|
138
|
-
h3. Rails Helpers in ViewModels
|
139
|
-
|
140
|
-
Use @helper@ as you would in the controller.
|
141
|
-
@helper ActionView::Helpers::UrlHelper@
|
142
|
-
@helper ApplicationHelper@
|
143
|
-
Note: It is helpful to create a superclass to all view models in the project with generally used helpers.
|
144
|
-
We use @ViewModels::Project@ a lot, for example. See example below.
|
145
|
-
|
146
|
-
h3. Controller Delegate Methods
|
147
|
-
|
148
|
-
Use @controller_method(*args)@.
|
149
|
-
|
150
|
-
Delegates current_user and logger on the view_model to the controller: @controller_method :current_user, :logger@
|
151
|
-
|
152
|
-
h2. The Generator
|
153
|
-
|
154
|
-
Generates view model class, spec, and views. Use as follows:
|
155
|
-
|
156
|
-
@script/generate view_models <view model class name>@
|
157
|
-
@script/generate view_models User@
|
158
|
-
|
159
|
-
@script/generate view_models <view model class name> <partials for render_as, separated by space>@
|
160
|
-
@script/generate view_models User compact extensive list_item table_item@
|
161
|
-
|
162
|
-
h2. One Big Fat Example
|
163
|
-
|
164
|
-
The following classes all have specs of course ;) But are not shown since they don't help the example.
|
165
|
-
|
166
|
-
@ViewModels@ superclass for this project.
|
167
|
-
|
168
|
-
We include all of Rails' helpers for the view models in this project.
|
169
|
-
Also, we include the @ApplicationHelper@.
|
170
|
-
|
171
|
-
We delegate @logger@ and @current_user@ calls in the view models to the active controller.
|
172
|
-
|
173
|
-
<pre>
|
174
|
-
<code>
|
175
|
-
class ViewModels::Project < ViewModels::Base
|
176
|
-
|
177
|
-
# Our ApplicationHelper.
|
178
|
-
#
|
179
|
-
helper ApplicationHelper
|
180
|
-
|
181
|
-
# We want to be able to call view_model_for in our view_models.
|
182
|
-
#
|
183
|
-
helper ViewModels::Helpers::Rails
|
184
|
-
|
185
|
-
# Include all common view helpers.
|
186
|
-
#
|
187
|
-
helper ViewModels::Helpers::View
|
188
|
-
|
189
|
-
# We want to be able to use id, dom_id, to_param on the view model.
|
190
|
-
#
|
191
|
-
# Note: Overrides the standard dom_id method from the RecordIdentificationHelper.
|
192
|
-
#
|
193
|
-
include ViewModels::Extensions::ActiveRecord
|
194
|
-
|
195
|
-
controller_method :logger, :current_user
|
196
|
-
|
197
|
-
end
|
198
|
-
|
199
|
-
# All items have a description that needs to be filtered by textilize.
|
200
|
-
#
|
201
|
-
class ViewModels::Item < ViewModels::Project
|
202
|
-
model_reader :description, :filter_through => :textilize
|
203
|
-
# Use price in the view as follows:
|
204
|
-
# = view_model.price - will display e.g. 16.57 CHF, since it is filtered first through localize_currency
|
205
|
-
model_reader :price, :filter_through => :localize_currency
|
206
|
-
|
207
|
-
# Converts a database price tag to the users chosen value, with the users preferred currency appended.
|
208
|
-
# If the user is Swiss, localize_currency will convert 10 Euros to "16.57 CHF"
|
209
|
-
#
|
210
|
-
def localize_currency(price_in_euros)
|
211
|
-
converted_price = current_user.convert_price(price_in_euros)
|
212
|
-
"#{converted_price} #{current_user.currency.to_s}"
|
213
|
-
end
|
214
|
-
end
|
215
|
-
|
216
|
-
# This class also has partial templates in the directory
|
217
|
-
# app/views/view_models/book
|
218
|
-
# that are called
|
219
|
-
# _cart_item.html.haml
|
220
|
-
# _cart_item.text.erb
|
221
|
-
#
|
222
|
-
# Call view_model_for on a book in the view or controller to get this view_model.
|
223
|
-
#
|
224
|
-
class ViewModels::Book < ViewModels::Item
|
225
|
-
model_reader :author, :title, :pages
|
226
|
-
model_reader :excerpt, :filter_through => :textilize
|
227
|
-
|
228
|
-
def header
|
229
|
-
content_tag(:h1, "#{author} – #{title}")
|
230
|
-
end
|
231
|
-
|
232
|
-
def full_description
|
233
|
-
content_tag(:p, "#{excerpt} #{description}", :class => 'description full')
|
234
|
-
end
|
235
|
-
end
|
31
|
+
h2. Links Galore!
|
236
32
|
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
class ViewModels::Toy < ViewModels::Item
|
246
|
-
model_reader :starting_age, :small_dangerous_parts
|
247
|
-
|
248
|
-
def obligatory_parental_warning
|
249
|
-
"Warning, this toy can only be used by kids ages #{starting_age} and up. Your department of health. Thank you."
|
250
|
-
end
|
251
|
-
|
252
|
-
end
|
253
|
-
</code>
|
254
|
-
</pre>
|
33
|
+
"Usage, Examples, In-depth Infos [Wiki]":http://wiki.github.com/floere/view_models/
|
34
|
+
"Reference [RDoc]":http://rdoc.info/projects/floere/view_models
|
35
|
+
"Gem [RubyGems.org]":http://rubygems.org/gems/view_models
|
36
|
+
"Mailing List":http://groups.google.com/group/view_models/topics
|
37
|
+
"Bug Tracker":http://github.com/floere/view_models/issues
|
38
|
+
"Metrics":http://getcaliper.com/caliper/project?repo=git://github.com/floere/view_models.git
|
39
|
+
"Source [Github]":http://github.com/floere/view_models
|
40
|
+
"Homepage":http://floere.github.com/view_models/
|
data/TODO.textile
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
h1. Next up
|
2
2
|
|
3
|
-
*
|
4
|
-
|
3
|
+
* Encapsule the rather bloated hierarchical rendering in a HierarchicalRendering object that mediates the whole rendering process.
|
4
|
+
|
5
|
+
--* Explore possibilities to include Modules in hierarchical rendering.--
|
data/VERSION.yml
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
module ModulesInRenderHierarchy
|
2
|
-
|
2
|
+
|
3
3
|
def self.included klass
|
4
4
|
klass.extend ClassMethods
|
5
5
|
klass.metaclass.alias_method_chain :include, :superclass_override
|
6
6
|
end
|
7
|
-
|
7
|
+
|
8
8
|
module ClassMethods
|
9
9
|
def include_with_superclass_override mod
|
10
10
|
original_superclass = superclass
|
@@ -17,5 +17,5 @@ module ModulesInRenderHierarchy
|
|
17
17
|
end
|
18
18
|
end
|
19
19
|
end
|
20
|
-
|
20
|
+
|
21
21
|
end
|
@@ -4,7 +4,7 @@ module ViewModels
|
|
4
4
|
#
|
5
5
|
module RenderOptions
|
6
6
|
|
7
|
-
#
|
7
|
+
# Base class for Partial and Template.
|
8
8
|
#
|
9
9
|
class Base
|
10
10
|
|
@@ -57,35 +57,43 @@ module ViewModels
|
|
57
57
|
template_name.to_s.include?('/') ? specific_path(template_name) : incomplete_path(template_name)
|
58
58
|
end
|
59
59
|
|
60
|
-
#
|
61
|
-
#
|
62
|
-
def deoptionize template_name
|
63
|
-
if template_name.kind_of?(Hash)
|
64
|
-
@options.merge! template_name
|
65
|
-
@options.delete :partial
|
66
|
-
else
|
67
|
-
template_name
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
#
|
60
|
+
# Specific path is a specifically named view path, with slashes.
|
72
61
|
#
|
73
62
|
def specific_path name
|
74
63
|
self.path = File.dirname name
|
75
64
|
self.name_with_prefix = File.basename name
|
76
65
|
end
|
77
66
|
|
78
|
-
#
|
67
|
+
# Incomplete path is a view path without slashes. The view models will
|
68
|
+
# try to find out where to take the template from.
|
79
69
|
#
|
80
70
|
def incomplete_path name
|
81
71
|
self.path = nil
|
82
72
|
self.name_with_prefix = name
|
83
73
|
end
|
84
74
|
|
75
|
+
# Add the prefix to the template name.
|
76
|
+
#
|
77
|
+
# Note: Will add an underscore if it's a partial.
|
78
|
+
#
|
85
79
|
def name_with_prefix= name
|
86
80
|
self.name = "#{self.prefix}#{name}"
|
87
81
|
end
|
88
82
|
|
83
|
+
# Extracts the template name from the given parameter.
|
84
|
+
#
|
85
|
+
# If it's a hash, it will remove and use the :partial option.
|
86
|
+
# If not, it will use that.
|
87
|
+
#
|
88
|
+
def deoptionize template_name
|
89
|
+
if template_name.kind_of?(Hash)
|
90
|
+
@options.merge! template_name
|
91
|
+
@options.delete :partial
|
92
|
+
else
|
93
|
+
template_name
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
89
97
|
end
|
90
98
|
|
91
99
|
# A specific container for partial rendering.
|
@@ -32,8 +32,6 @@ describe ViewModels::Helpers::Rails do
|
|
32
32
|
specific_view_model_mapping.should == {}
|
33
33
|
end
|
34
34
|
it "should raise an ArgumentError on an non-mapped model" do
|
35
|
-
# TODO really clear enough that one should provide a ViewModel with an initializer with 2 params?
|
36
|
-
#
|
37
35
|
class SomeViewModelClass; end
|
38
36
|
specific_view_model_mapping[String] = SomeViewModelClass
|
39
37
|
lambda {
|
@@ -43,8 +41,6 @@ describe ViewModels::Helpers::Rails do
|
|
43
41
|
end
|
44
42
|
describe "no specific mapping" do
|
45
43
|
it "should raise on an non-mapped model" do
|
46
|
-
# TODO really clear enough that the view model class is missing?
|
47
|
-
#
|
48
44
|
lambda {
|
49
45
|
view_model_for(42)
|
50
46
|
}.should raise_error(NameError, "uninitialized constant ViewModels::Fixnum")
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 1
|
7
7
|
- 5
|
8
|
-
-
|
9
|
-
version: 1.5.
|
8
|
+
- 7
|
9
|
+
version: 1.5.7
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Florian Hanke
|
@@ -17,7 +17,7 @@ autorequire:
|
|
17
17
|
bindir: bin
|
18
18
|
cert_chain: []
|
19
19
|
|
20
|
-
date: 2010-
|
20
|
+
date: 2010-04-15 00:00:00 +02:00
|
21
21
|
default_executable:
|
22
22
|
dependencies:
|
23
23
|
- !ruby/object:Gem::Dependency
|