view_models 2.0.1 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.bundle/config +2 -0
- data/.gitignore +8 -0
- data/.rspec +2 -0
- data/.rvmrc +1 -0
- data/.travis.yml +19 -0
- data/.yardoc/checksums +13 -0
- data/.yardoc/object_types +0 -0
- data/.yardoc/objects/root.dat +0 -0
- data/.yardoc/proxy_types +0 -0
- data/Appraisals +15 -0
- data/CHANGELOG +31 -0
- data/Gemfile +13 -0
- data/Gemfile.lock +174 -0
- data/MIT-LICENSE +20 -0
- data/README.textile +199 -0
- data/Rakefile +24 -0
- data/doc/ModulesInRenderHierarchy.html +186 -0
- data/doc/ModulesInRenderHierarchy/ClassMethods.html +188 -0
- data/doc/ViewModels.html +202 -0
- data/doc/ViewModels/Base.html +1342 -0
- data/doc/ViewModels/ContextExtractor.html +406 -0
- data/doc/ViewModels/Extensions.html +128 -0
- data/doc/ViewModels/Extensions/ModelReader.html +255 -0
- data/doc/ViewModels/Extensions/ModelReader/FilteredDelegationInstaller.html +908 -0
- data/doc/ViewModels/Extensions/ModelReader/Options.html +551 -0
- data/doc/ViewModels/Extensions/View.html +303 -0
- data/doc/ViewModels/Helpers.html +129 -0
- data/doc/ViewModels/Helpers/Mapping.html +562 -0
- data/doc/ViewModels/Helpers/Mapping/Collection.html +844 -0
- data/doc/ViewModels/Helpers/View.html +287 -0
- data/doc/ViewModels/PathStore.html +745 -0
- data/doc/ViewModels/RenderOptions.html +126 -0
- data/doc/ViewModels/RenderOptions/Base.html +1187 -0
- data/doc/ViewModels/RenderOptions/Partial.html +231 -0
- data/doc/ViewModels/RenderOptions/Template.html +231 -0
- data/doc/ViewModels/View.html +414 -0
- data/doc/ViewModelsGenerator.html +410 -0
- data/doc/_index.html +317 -0
- data/doc/class_list.html +53 -0
- data/doc/css/common.css +1 -0
- data/doc/css/full_list.css +57 -0
- data/doc/css/style.css +328 -0
- data/doc/file.README.html +113 -0
- data/doc/file_list.html +55 -0
- data/doc/frames.html +28 -0
- data/doc/index.html +113 -0
- data/doc/js/app.js +214 -0
- data/doc/js/full_list.js +173 -0
- data/doc/js/jquery.js +4 -0
- data/doc/method_list.html +628 -0
- data/doc/top-level-namespace.html +114 -0
- data/feature_support/testapp/app/controllers/heroes_controller.rb +6 -0
- data/feature_support/testapp/app/controllers/users_controller.rb +6 -0
- data/feature_support/testapp/app/helpers/application_helper.rb +8 -0
- data/feature_support/testapp/app/models/hero.rb +8 -0
- data/feature_support/testapp/app/models/user.rb +3 -0
- data/feature_support/testapp/app/view_models/hero.rb +8 -0
- data/feature_support/testapp/app/view_models/test.rb +8 -0
- data/feature_support/testapp/app/view_models/user.rb +14 -0
- data/feature_support/testapp/app/views/heroes/show.html.slim +6 -0
- data/feature_support/testapp/app/views/users/_box.html.slim +8 -0
- data/feature_support/testapp/app/views/users/show.html.slim +6 -0
- data/feature_support/testapp/config/application.rb +64 -0
- data/feature_support/testapp/config/cucumber.yml +8 -0
- data/feature_support/testapp/config/database.yml +28 -0
- data/feature_support/testapp/config/environments/cucumber.rb +11 -0
- data/feature_support/testapp/config/environments/test.rb +27 -0
- data/feature_support/testapp/config/routes.rb +4 -0
- data/feature_support/testapp/db/migrate/1_create_users.rb +13 -0
- data/feature_support/testapp/features/inheritance.feature +16 -0
- data/feature_support/testapp/features/step_definitions/testapp_steps.rb +16 -0
- data/feature_support/testapp/features/support/env.rb +13 -0
- data/feature_support/testapp/features/users.feature +15 -0
- data/feature_support/testapp/lib/tasks/cucumber.rake +31 -0
- data/feature_support/testapp/spec/factories/heroes.rb +7 -0
- data/feature_support/testapp/spec/factories/users.rb +7 -0
- data/features/rails_integration.feature +21 -0
- data/features/step_definitions/rails_steps.rb +112 -0
- data/features/support/env.rb +6 -0
- data/gemfiles/3.0.Gemfile +13 -0
- data/gemfiles/3.1.Gemfile +15 -0
- data/gemfiles/3.2.gemfile +15 -0
- data/lib/rails/generators/view_models/USAGE +6 -0
- data/lib/rails/generators/view_models/templates/views/_collection.html.erb +6 -0
- data/lib/{rails2/generators/view_models/templates/views/view_models/collection → rails/generators/view_models/templates/views}/_collection.html.haml +1 -2
- data/lib/rails/generators/view_models/templates/views/_collection.html.slim +6 -0
- data/lib/{rails2/generators/view_models/templates/views/_empty.html.haml → rails/generators/view_models/templates/views/_empty.html.erb} +0 -0
- data/lib/rails/generators/view_models/templates/views/_empty.html.haml +0 -0
- data/lib/rails/generators/view_models/templates/views/_empty.html.slim +0 -0
- data/lib/rails/generators/view_models/templates/views/_list.html.erb +6 -0
- data/lib/rails/generators/view_models/templates/views/_list.html.haml +4 -0
- data/lib/rails/generators/view_models/templates/views/_list.html.slim +5 -0
- data/lib/rails/generators/view_models/templates/views/_pagination.html.erb +18 -0
- data/lib/{rails2/generators/view_models/templates/views/view_models/collection → rails/generators/view_models/templates/views}/_pagination.html.haml +2 -2
- data/lib/rails/generators/view_models/templates/views/_pagination.html.slim +12 -0
- data/lib/rails/generators/view_models/templates/views/_table.html.erb +10 -0
- data/lib/{rails2/generators/view_models/templates/views/view_models/collection → rails/generators/view_models/templates/views}/_table.html.haml +0 -0
- data/lib/rails/generators/view_models/templates/views/_table.html.slim +5 -0
- data/lib/rails/generators/view_models/view_models_generator.rb +57 -0
- data/lib/view_models.rb +15 -5
- data/lib/{shared/lib/view_models → view_models}/base.rb +148 -37
- data/lib/{shared/lib/view_models → view_models}/context_extractor.rb +6 -2
- data/lib/{shared/lib/view_models → view_models}/extensions/model_reader.rb +27 -13
- data/lib/{rails2/lib → view_models}/extensions/view.rb +6 -1
- data/lib/view_models/helpers/collection.rb +113 -0
- data/lib/{shared/lib/view_models → view_models}/helpers/mapping.rb +12 -15
- data/lib/{rails2/lib → view_models}/helpers/view.rb +5 -1
- data/lib/{shared/lib/view_models → view_models}/path_store.rb +6 -1
- data/lib/{shared/lib/view_models → view_models}/render_options.rb +19 -4
- data/lib/view_models/version.rb +5 -0
- data/lib/{rails2/lib/view_models → view_models}/view.rb +7 -6
- data/spec/{rails2/lib → lib}/view_models/base_spec.rb +38 -5
- data/spec/{rails2/lib → lib/view_models}/extensions/model_reader_spec.rb +4 -4
- data/spec/{shared/lib → lib/view_models}/helpers/collection_spec.rb +21 -1
- data/spec/{shared/lib → lib/view_models}/helpers/mapping_spec.rb +6 -5
- data/spec/{rails2/lib → lib/view_models}/helpers/view_spec.rb +1 -1
- data/spec/lib/view_models/view_spec.rb +42 -0
- data/spec/spec_helper.rb +32 -0
- data/spec/spec_helper_extensions.rb +13 -0
- data/view_models.gemspec +36 -0
- metadata +411 -142
- data/lib/init.rb +0 -1
- data/lib/padrino/README.textile +0 -3
- data/lib/padrino/init.rb +0 -10
- data/lib/padrino/lib/helpers/collection.rb +0 -27
- data/lib/padrino/lib/padrino/view_models.rb +0 -19
- data/lib/padrino/lib/view_models/base.rb +0 -65
- data/lib/rails2/README.textile +0 -3
- data/lib/rails2/TODO.textile +0 -5
- data/lib/rails2/generators/view_models/USAGE +0 -6
- data/lib/rails2/generators/view_models/templates/README +0 -1
- data/lib/rails2/generators/view_models/templates/spec/view_model_spec.rb +0 -7
- data/lib/rails2/generators/view_models/templates/view_models/view_model.rb +0 -5
- data/lib/rails2/generators/view_models/templates/views/view_models/collection/_collection.html.erb +0 -1
- data/lib/rails2/generators/view_models/templates/views/view_models/collection/_collection.text.erb +0 -6
- data/lib/rails2/generators/view_models/templates/views/view_models/collection/_list.html.erb +0 -1
- data/lib/rails2/generators/view_models/templates/views/view_models/collection/_list.text.erb +0 -6
- data/lib/rails2/generators/view_models/templates/views/view_models/collection/_pagination.text.erb +0 -3
- data/lib/rails2/generators/view_models/templates/views/view_models/collection/_table.text.erb +0 -10
- data/lib/rails2/generators/view_models/view_models_generator.rb +0 -47
- data/lib/rails2/init.rb +0 -18
- data/lib/rails2/lib/experimental/README.textile +0 -32
- data/lib/rails2/lib/experimental/modules_in_render_hierarchy.rb +0 -21
- data/lib/rails2/lib/helpers/collection.rb +0 -27
- data/lib/rails2/lib/view_models.rb +0 -1
- data/lib/rails2/lib/view_models/base.rb +0 -99
- data/lib/shared/README.textile +0 -3
- data/lib/shared/init.rb +0 -11
- data/lib/shared/lib/view_models.rb +0 -4
- data/lib/shared/lib/view_models/extensions/active_record.rb +0 -27
- data/lib/shared/lib/view_models/helpers/collection.rb +0 -110
- data/spec/padrino/integration/integration_spec.rb +0 -287
- data/spec/padrino/lib/helpers/collection_spec.rb +0 -30
- data/spec/rails2/integration/integration_spec.rb +0 -279
- data/spec/rails2/lib/extensions/active_record_spec.rb +0 -31
- data/spec/rails2/lib/helpers/collection_spec.rb +0 -30
- data/spec/rails2/lib/view_models/view_spec.rb +0 -12
@@ -1,59 +1,115 @@
|
|
1
1
|
# Base Module for ViewModels.
|
2
|
+
# @author Florian Hanke
|
3
|
+
# @author Kaspar Schiess
|
4
|
+
# @author Niko Dittmann
|
5
|
+
# @author Beat Richartz
|
6
|
+
# @version 3.0.0
|
7
|
+
# @since 1.0.0
|
2
8
|
#
|
3
|
-
module ViewModels
|
4
|
-
|
9
|
+
module ViewModels
|
10
|
+
|
5
11
|
# Gets raised when render_as, render_the, or render_template cannot
|
6
12
|
# find the named template, not even in the hierarchy.
|
7
13
|
#
|
8
|
-
|
14
|
+
MissingTemplateError = Class.new(StandardError)
|
9
15
|
|
10
16
|
# Base class from which all view_models inherit.
|
17
|
+
# @example Create Your first View Model
|
18
|
+
# class ViewModels::MyViewModel < ViewModels::Base
|
19
|
+
# # your code
|
20
|
+
# end
|
11
21
|
#
|
12
22
|
class Base
|
13
23
|
|
24
|
+
# Make the rails methods helper and helper_method available in view models
|
25
|
+
#
|
26
|
+
include AbstractController::Helpers
|
27
|
+
|
14
28
|
# Create a view_model. To create a view_model, you need to have a model (to present) and a context.
|
15
29
|
# The context is usually a view, a controller, or an app, but doesn't need to be.
|
16
30
|
#
|
17
|
-
#
|
31
|
+
# The @context = @controller is really only needed because some Rails helpers access @controller directly.
|
32
|
+
# It's really bad.
|
33
|
+
# @param [ActiveRecord] model The model which the view model is based upon
|
34
|
+
# @param [ActionView, ActionController, Rails::Application] app_or_controller_or_view The context of the view model
|
35
|
+
# @example Initialize a view model without the mapping helper
|
36
|
+
# ViewModel::YourModel.new(@model, @context)
|
18
37
|
#
|
19
38
|
def initialize model, app_or_controller_or_view
|
20
|
-
@model
|
21
|
-
@context = ContextExtractor.new(app_or_controller_or_view).extract
|
39
|
+
@model = model
|
40
|
+
@context = @controller = ContextExtractor.new(app_or_controller_or_view).extract
|
22
41
|
end
|
23
42
|
|
24
43
|
class << self
|
25
44
|
|
26
|
-
#
|
27
|
-
# template inheritance, to remember specific
|
28
|
-
# [path, name, format] tuples, pointing to a template path,
|
29
|
-
# so the view models don't have to traverse the inheritance chain always.
|
45
|
+
# The path store accessor, storing the view paths for the view model
|
30
46
|
#
|
31
47
|
attr_accessor :path_store
|
48
|
+
|
49
|
+
# Installs a path store, a specific store for template inheritance, to remember specific
|
50
|
+
# [path, name, format] tuples, pointing to a template path
|
51
|
+
# so the view models don't have to always traverse the inheritance chain.
|
52
|
+
# @param [ViewModel] subclass The subclass of the view model
|
53
|
+
#
|
32
54
|
def inherited subclass
|
33
55
|
ViewModels::PathStore.install_in subclass
|
34
56
|
super
|
35
57
|
end
|
36
58
|
|
37
59
|
# Delegates method calls to the context.
|
38
|
-
#
|
39
|
-
# Examples:
|
40
|
-
# context_method :current_user
|
41
|
-
# context_method :current_user, :current_profile # multiple methods to be delegated
|
42
|
-
#
|
43
60
|
# In the view_model:
|
44
61
|
# self.current_user
|
45
62
|
# will call
|
46
63
|
# context.current_user
|
47
64
|
#
|
65
|
+
# @param [Symbol] methods A list of symbols representing methods to be delegated
|
66
|
+
# @example delegate one method to the context
|
67
|
+
# context_method :current_user
|
68
|
+
# @example delegate multiple methods to the context
|
69
|
+
# context_method :current_user, :current_profile
|
70
|
+
#
|
48
71
|
def context_method *methods
|
49
72
|
delegate *methods << { :to => :context }
|
50
73
|
end
|
51
74
|
|
75
|
+
# Alias the context_method to the rails-centric controller_method.
|
76
|
+
#
|
77
|
+
alias controller_method context_method
|
78
|
+
|
52
79
|
# Installs the model_reader Method for filtered
|
53
80
|
# model method delegation.
|
54
81
|
#
|
55
82
|
include Extensions::ModelReader
|
56
83
|
|
84
|
+
unless instance_methods.include?('old_add_template_helper')
|
85
|
+
alias old_add_template_helper add_template_helper
|
86
|
+
|
87
|
+
# Wrapper for add_template_helper in ActionController::Helpers, also
|
88
|
+
# includes given helper in the view_model
|
89
|
+
#
|
90
|
+
# @todo extract into module
|
91
|
+
# @param [Module] helper_module the helper to be added
|
92
|
+
#
|
93
|
+
def add_template_helper helper_module
|
94
|
+
include helper_module
|
95
|
+
old_add_template_helper helper_module
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# Sets the view format and tries to render the given options.
|
100
|
+
# @param [ActionView] renderer The view renderer
|
101
|
+
# @param [Hash] options The options to pass to the view
|
102
|
+
# @option options [Hash] :locals The locals to pass to the view
|
103
|
+
# @note Also caches [path, name, format] => template path.
|
104
|
+
#
|
105
|
+
def render renderer, options
|
106
|
+
options.format! renderer
|
107
|
+
path_store.cached options do
|
108
|
+
options.file = template_path renderer, options
|
109
|
+
renderer.render_with options
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
57
113
|
protected
|
58
114
|
|
59
115
|
# Hierarchical rendering.
|
@@ -61,27 +117,31 @@ module ViewModels
|
|
61
117
|
|
62
118
|
# Returns the next view model class in the render hierarchy.
|
63
119
|
#
|
64
|
-
#
|
120
|
+
# @note Just returns the superclass.
|
65
121
|
#
|
66
|
-
#
|
122
|
+
# @todo Think about raising the MissingTemplateError here.
|
123
|
+
# @return The superclass of the view model, which ends with ViewModel::Base
|
67
124
|
#
|
68
125
|
def next_in_render_hierarchy
|
69
126
|
superclass
|
70
127
|
end
|
71
|
-
|
128
|
+
|
129
|
+
# Raises the fitting template error with the given message
|
130
|
+
# @param [String,Symbol] message The message with which the template error should be raised
|
131
|
+
# @raise [MissingTemplateError] A template error indicating that the template is missing
|
72
132
|
#
|
73
133
|
def raise_template_error_with message
|
74
134
|
raise MissingTemplateError.new("No template #{message} found.")
|
75
135
|
end
|
136
|
+
|
76
137
|
# Check if the view lookup inheritance chain has ended.
|
77
|
-
#
|
78
138
|
# Raises a MissingTemplateError if yes.
|
139
|
+
# @return wether the current class is ViewModel::Base and therefore at the end of the inheritance chain
|
79
140
|
#
|
80
141
|
def inheritance_chain_ends?
|
81
142
|
self == ViewModels::Base
|
82
143
|
end
|
83
144
|
|
84
|
-
|
85
145
|
# Template finding
|
86
146
|
#
|
87
147
|
|
@@ -91,27 +151,36 @@ module ViewModels
|
|
91
151
|
#
|
92
152
|
# Raises a MissingTemplateError if none is found during
|
93
153
|
# inheritance chain traversal.
|
154
|
+
# @param [ActionView] renderer The view renderer
|
155
|
+
# @param [Hash] options The options to pass to the template path
|
94
156
|
#
|
95
|
-
def template_path renderer, options
|
157
|
+
def template_path renderer, options
|
96
158
|
raise_template_error_with options.error_message if inheritance_chain_ends?
|
97
159
|
|
98
160
|
template_path_from(renderer, options) || self.next_in_render_hierarchy.template_path(renderer, options)
|
99
161
|
end
|
100
|
-
|
101
|
-
#
|
102
|
-
|
103
|
-
#
|
162
|
+
|
163
|
+
# Accesses the view to find a suitable template path.
|
164
|
+
# @param [ActionView] renderer The view renderer
|
165
|
+
# @param [Hash] options The options to pass to the view
|
104
166
|
#
|
105
|
-
|
106
|
-
|
107
|
-
|
167
|
+
def template_path_from renderer, options
|
168
|
+
template = renderer.find_template tentative_template_path(options)
|
169
|
+
|
170
|
+
template && template.virtual_path.to_s
|
171
|
+
end
|
172
|
+
|
173
|
+
# @return render path either a stored path or a newly generated one.
|
174
|
+
# @note If nothing or nil is passed, the store is ignored.
|
175
|
+
# @param [Hash] options The view render options
|
108
176
|
#
|
109
177
|
def tentative_template_path options
|
110
178
|
path_store[options.path_key] || generate_template_path_from(options)
|
111
179
|
end
|
112
180
|
|
113
|
-
#
|
114
|
-
#
|
181
|
+
# @return the root of this view_models views with the template name appended.
|
182
|
+
# @example Returning a specific path
|
183
|
+
# 'some/specific/path/to/template'
|
115
184
|
#
|
116
185
|
def generate_template_path_from options
|
117
186
|
File.join generate_path_from(options), options.name
|
@@ -130,19 +199,47 @@ module ViewModels
|
|
130
199
|
# If the class is named
|
131
200
|
# ViewModels::Models::Book
|
132
201
|
# this method will yield
|
133
|
-
#
|
202
|
+
# models/book
|
134
203
|
#
|
135
204
|
# Note: Remembers the result since it is dependent on the Class name only.
|
136
205
|
#
|
137
206
|
def view_model_path
|
138
|
-
@view_model_path
|
207
|
+
@view_model_path ||= self.name.gsub(/^ViewModels::|(\w+)(::)?/) { $1.underscore.pluralize + ($2 ? '/' : '').to_s if $1 }
|
139
208
|
end
|
140
209
|
|
141
210
|
end
|
142
|
-
|
211
|
+
|
143
212
|
# Delegate context methods.
|
144
213
|
#
|
145
|
-
context_method :logger
|
214
|
+
context_method :logger, :form_authenticity_token, :protect_against_forgery?, :request_forgery_protection_token, :config, :cookies, :flash, :default_url_options
|
215
|
+
|
216
|
+
# id and param are simply delegated to the model.
|
217
|
+
#
|
218
|
+
# This makes it possible to use the view_model
|
219
|
+
# for e.g. url generation:
|
220
|
+
# * edit_user_path(view_model)
|
221
|
+
#
|
222
|
+
delegate :id, :to_param, :to => :model
|
223
|
+
|
224
|
+
# Delegate dom id to the action controller record identifier.
|
225
|
+
#
|
226
|
+
def dom_id *args
|
227
|
+
if args.present?
|
228
|
+
context.dom_id *args
|
229
|
+
else
|
230
|
+
ActionController::RecordIdentifier.dom_id model
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
# Make all the dynamically generated routes (restful routes etc.)
|
235
|
+
# available in the view_model
|
236
|
+
#
|
237
|
+
Rails.application.routes.install_helpers self if Rails.application
|
238
|
+
|
239
|
+
# include these helpers by default
|
240
|
+
#
|
241
|
+
helper Helpers::View
|
242
|
+
helper Helpers::Mapping
|
146
243
|
|
147
244
|
# Renders the given partial in the view_model's view root in the format given.
|
148
245
|
#
|
@@ -199,6 +296,22 @@ module ViewModels
|
|
199
296
|
#
|
200
297
|
attr_reader :model, :context
|
201
298
|
|
299
|
+
# CaptureHelper needs this.
|
300
|
+
#
|
301
|
+
attr_accessor :output_buffer
|
302
|
+
|
303
|
+
# alias the context as controller
|
304
|
+
#
|
305
|
+
alias controller context
|
306
|
+
|
307
|
+
# Returns a view instance for render_xxx.
|
308
|
+
#
|
309
|
+
# @todo Try getting a view instance from the controller.
|
310
|
+
#
|
311
|
+
def renderer
|
312
|
+
View.new context, self._helpers
|
313
|
+
end
|
314
|
+
|
202
315
|
# Internal render method that uses the options to get a view instance
|
203
316
|
# and then referring to its class for rendering.
|
204
317
|
#
|
@@ -212,9 +325,7 @@ module ViewModels
|
|
212
325
|
|
213
326
|
# Determines what format to use for rendering.
|
214
327
|
#
|
215
|
-
#
|
216
|
-
# if none is explicitly set in the options.
|
217
|
-
# This propagates the format to further render_xxx calls.
|
328
|
+
# @note Uses the template format of the view model instance if none is explicitly set in the options. This propagates the format to further render_xxx calls.
|
218
329
|
#
|
219
330
|
def determine_and_set_format options
|
220
331
|
options.format = @template_format = options.format || @template_format
|
@@ -4,17 +4,21 @@ module ViewModels
|
|
4
4
|
|
5
5
|
# Extracts controllers for a living from unsuspecting views.
|
6
6
|
#
|
7
|
-
# Note: This is actually only needed in Rails. In Padrino, the context is always the app.
|
8
|
-
#
|
9
7
|
class ContextExtractor
|
10
8
|
|
9
|
+
# The context
|
10
|
+
#
|
11
11
|
attr_reader :context
|
12
12
|
|
13
|
+
# Initialize the Context extractor
|
14
|
+
# @param [ActionController, ActionMailer, ActionView] context Some render context
|
15
|
+
#
|
13
16
|
def initialize context
|
14
17
|
@context = context
|
15
18
|
end
|
16
19
|
|
17
20
|
# Extracts a controller from the context.
|
21
|
+
# @return [ActionController] an instance of action controller
|
18
22
|
#
|
19
23
|
def extract
|
20
24
|
context = self.context
|
@@ -2,15 +2,23 @@
|
|
2
2
|
#
|
3
3
|
#
|
4
4
|
module ViewModels
|
5
|
+
|
6
|
+
# Extensions of the View Model Class
|
7
|
+
#
|
5
8
|
module Extensions
|
9
|
+
|
10
|
+
# Model Reader extension. Allows to define model readers on view models, accessing attributes and methods
|
11
|
+
# on models
|
12
|
+
#
|
6
13
|
module ModelReader
|
7
14
|
|
8
15
|
# Define a reader for a model attribute. Acts as a filtered delegation to the model.
|
9
16
|
#
|
10
17
|
# You may specify a :filter_through option that is either a symbol or an array of symbols. The return value
|
11
|
-
# from the model will be filtered through the functions (arity 1) and then passed back to the receiver.
|
18
|
+
# from the model will be filtered through the functions (arity 1) and then passed back to the receiver.
|
19
|
+
# @param [Symbol, Hash] attributes_and_options The attributes and options for the model reader
|
12
20
|
#
|
13
|
-
#
|
21
|
+
# @example install different model readers
|
14
22
|
#
|
15
23
|
# model_reader :foobar # same as delegate :foobar, :to => :model
|
16
24
|
# model_reader :foobar, :filter_through => :h # html escape foobar
|
@@ -32,6 +40,7 @@ module ViewModels
|
|
32
40
|
end
|
33
41
|
|
34
42
|
# Extract filter_through options from the options hash if there are any.
|
43
|
+
# @param [Hash] options the options to split
|
35
44
|
#
|
36
45
|
def split options
|
37
46
|
@filters = if options.last.kind_of?(Hash)
|
@@ -42,6 +51,9 @@ module ViewModels
|
|
42
51
|
@attributes = options
|
43
52
|
end
|
44
53
|
|
54
|
+
# Render the options to an array
|
55
|
+
# @return [Array] The attributes and the filters in this order
|
56
|
+
#
|
45
57
|
def to_a
|
46
58
|
[attributes, filters]
|
47
59
|
end
|
@@ -53,13 +65,19 @@ module ViewModels
|
|
53
65
|
#
|
54
66
|
class FilteredDelegationInstaller
|
55
67
|
|
68
|
+
# The attributes target, attributes, filters
|
69
|
+
#
|
56
70
|
attr_reader :target, :attributes, :filters
|
57
71
|
|
72
|
+
# Initialize a new filtered delegation
|
73
|
+
# @param [ViewModel] target the target to install the filtered delegation in
|
74
|
+
# @param [ModelReader::Options] options The options for the filtered delegation
|
75
|
+
#
|
58
76
|
def initialize target, options
|
59
77
|
@target, @attributes, @filters = target, *options
|
60
78
|
end
|
61
79
|
|
62
|
-
# Install
|
80
|
+
# Install a reader for each attribute
|
63
81
|
#
|
64
82
|
def install
|
65
83
|
attributes.each { |attribute| install_reader(attribute) }
|
@@ -67,10 +85,8 @@ module ViewModels
|
|
67
85
|
|
68
86
|
# Install a reader for the given name with the given filters.
|
69
87
|
#
|
70
|
-
#
|
71
|
-
#
|
72
|
-
# #
|
73
|
-
# * install_reader :attribute
|
88
|
+
# @example Installs a reader for model.attribute
|
89
|
+
# install_reader :attribute
|
74
90
|
#
|
75
91
|
def install_reader attribute
|
76
92
|
target.class_eval reader_definition_for(attribute)
|
@@ -79,7 +95,7 @@ module ViewModels
|
|
79
95
|
# Defines a reader for the given model attribute and filtering
|
80
96
|
# through the given filters.
|
81
97
|
#
|
82
|
-
#
|
98
|
+
# @note The filters are applied from last to first element.
|
83
99
|
#
|
84
100
|
def reader_definition_for attribute
|
85
101
|
"def #{attribute}; #{filtered_left_parentheses}model.#{attribute}#{right_parentheses}; end"
|
@@ -88,7 +104,7 @@ module ViewModels
|
|
88
104
|
# Combines left parentheses and filters.
|
89
105
|
#
|
90
106
|
def filtered_left_parentheses
|
91
|
-
filters.zip(left_parentheses).
|
107
|
+
filters.zip(left_parentheses).join
|
92
108
|
end
|
93
109
|
|
94
110
|
# Generates the needed amount of parentheses to match the left parentheses.
|
@@ -99,10 +115,8 @@ module ViewModels
|
|
99
115
|
|
100
116
|
# Generates an array of left parentheses with
|
101
117
|
# length <amount of filters>
|
102
|
-
#
|
103
|
-
# #
|
104
|
-
# #
|
105
|
-
# left_parentheses # => ['(', '(', '(', '(']
|
118
|
+
# @example for 4 Filters
|
119
|
+
# left_parentheses # => ['(', '(', '(', '(']
|
106
120
|
#
|
107
121
|
def left_parentheses
|
108
122
|
['('] * filters.size
|
@@ -5,15 +5,20 @@
|
|
5
5
|
#
|
6
6
|
module ViewModels
|
7
7
|
module Extensions
|
8
|
+
|
9
|
+
# Extensions for the View instance
|
10
|
+
#
|
8
11
|
module View
|
9
12
|
|
10
|
-
#
|
13
|
+
# Renders the template with the given options
|
14
|
+
# @param [RenderOptions::Base] options The options to render with
|
11
15
|
#
|
12
16
|
def render_with options
|
13
17
|
render options.to_render_options
|
14
18
|
end
|
15
19
|
|
16
20
|
# Finds the template in the view paths at the given path, with its format.
|
21
|
+
# @param [String] path the template path
|
17
22
|
#
|
18
23
|
def find_template path
|
19
24
|
view_paths.find_template path, template_format rescue nil
|