brainstem 0.2.6.1 → 1.0.0.pre.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -13
- data/CHANGELOG.md +16 -2
- data/Gemfile.lock +51 -36
- data/README.md +531 -110
- data/brainstem.gemspec +6 -2
- data/lib/brainstem.rb +25 -9
- data/lib/brainstem/concerns/controller_param_management.rb +22 -0
- data/lib/brainstem/concerns/error_presentation.rb +58 -0
- data/lib/brainstem/concerns/inheritable_configuration.rb +29 -0
- data/lib/brainstem/concerns/lookup.rb +30 -0
- data/lib/brainstem/concerns/presenter_dsl.rb +111 -0
- data/lib/brainstem/controller_methods.rb +17 -8
- data/lib/brainstem/dsl/association.rb +55 -0
- data/lib/brainstem/dsl/associations_block.rb +12 -0
- data/lib/brainstem/dsl/base_block.rb +31 -0
- data/lib/brainstem/dsl/conditional.rb +25 -0
- data/lib/brainstem/dsl/conditionals_block.rb +15 -0
- data/lib/brainstem/dsl/configuration.rb +112 -0
- data/lib/brainstem/dsl/field.rb +68 -0
- data/lib/brainstem/dsl/fields_block.rb +25 -0
- data/lib/brainstem/preloader.rb +98 -0
- data/lib/brainstem/presenter.rb +325 -134
- data/lib/brainstem/presenter_collection.rb +82 -286
- data/lib/brainstem/presenter_validator.rb +96 -0
- data/lib/brainstem/query_strategies/README.md +107 -0
- data/lib/brainstem/query_strategies/base_strategy.rb +62 -0
- data/lib/brainstem/query_strategies/filter_and_search.rb +50 -0
- data/lib/brainstem/query_strategies/filter_or_search.rb +103 -0
- data/lib/brainstem/test_helpers.rb +5 -1
- data/lib/brainstem/version.rb +1 -1
- data/spec/brainstem/concerns/controller_param_management_spec.rb +42 -0
- data/spec/brainstem/concerns/error_presentation_spec.rb +113 -0
- data/spec/brainstem/concerns/inheritable_configuration_spec.rb +210 -0
- data/spec/brainstem/concerns/presenter_dsl_spec.rb +412 -0
- data/spec/brainstem/controller_methods_spec.rb +15 -27
- data/spec/brainstem/dsl/association_spec.rb +123 -0
- data/spec/brainstem/dsl/conditional_spec.rb +93 -0
- data/spec/brainstem/dsl/configuration_spec.rb +1 -0
- data/spec/brainstem/dsl/field_spec.rb +212 -0
- data/spec/brainstem/preloader_spec.rb +137 -0
- data/spec/brainstem/presenter_collection_spec.rb +565 -244
- data/spec/brainstem/presenter_spec.rb +726 -167
- data/spec/brainstem/presenter_validator_spec.rb +209 -0
- data/spec/brainstem/query_strategies/filter_and_search_spec.rb +46 -0
- data/spec/brainstem/query_strategies/filter_or_search_spec.rb +45 -0
- data/spec/spec_helper.rb +11 -3
- data/spec/spec_helpers/db.rb +32 -65
- data/spec/spec_helpers/presenters.rb +124 -29
- data/spec/spec_helpers/rr.rb +11 -0
- data/spec/spec_helpers/schema.rb +115 -0
- metadata +126 -30
- data/lib/brainstem/association_field.rb +0 -53
- data/lib/brainstem/engine.rb +0 -4
- data/pkg/brainstem-0.2.5.gem +0 -0
- data/pkg/brainstem-0.2.6.gem +0 -0
- data/spec/spec_helpers/cleanup.rb +0 -23
@@ -1,5 +1,5 @@
|
|
1
|
-
require 'brainstem/association_field'
|
2
1
|
require 'brainstem/search_unavailable_error'
|
2
|
+
require 'brainstem/presenter_validator'
|
3
3
|
|
4
4
|
module Brainstem
|
5
5
|
class PresenterCollection
|
@@ -12,102 +12,92 @@ module Brainstem
|
|
12
12
|
# @return [Integer] The default number of objects that will be returned in the presented hash.
|
13
13
|
attr_accessor :default_per_page
|
14
14
|
|
15
|
+
attr_accessor :default_max_filter_and_search_page
|
16
|
+
|
15
17
|
# @!visibility private
|
16
18
|
def initialize
|
17
19
|
@default_per_page = 20
|
18
20
|
@default_max_per_page = 200
|
21
|
+
@default_max_filter_and_search_page = 10_000 # TODO: figure out a better default and make it configurable
|
19
22
|
end
|
20
23
|
|
21
24
|
# The main presentation method, converting a model name and an optional scope into a hash structure, ready to be converted into JSON.
|
22
25
|
# If searching, Brainstem filtering, only, pagination, and ordering are skipped and should be implemented with your search solution.
|
23
|
-
# All request options are passed to the +
|
24
|
-
# @param [Class, String] name
|
26
|
+
# All request options are passed to the +search block+ for your convenience.
|
27
|
+
# @param [Class, String] name Either the ActiveRecord Class itself, or its pluralized table name as a string.
|
25
28
|
# @param [Hash] options The options that will be applied as the objects are converted.
|
26
29
|
# @option options [Hash] :params The +params+ hash included in a request for the presented object.
|
27
30
|
# @option options [ActiveRecord::Base] :model The model that is being presented (if different from +name+).
|
28
|
-
# @option options [String] :as The top-level key the presented objects will be assigned to (if different from +name.tableize+)
|
29
31
|
# @option options [Integer] :max_per_page The maximum number of items that can be requested by <code>params[:per_page]</code>.
|
30
32
|
# @option options [Integer] :per_page The number of items that will be returned if <code>params[:per_page]</code> is not set.
|
31
33
|
# @option options [Boolean] :apply_default_filters Determine if Presenter's filter defaults should be applied. On by default.
|
34
|
+
# @option options [Brainstem::Presenter] :primary_presenter The Presenter to use for filters and sorts. If unspecified, the +:model+ or +name+ will be used to find an appropriate Presenter.
|
32
35
|
# @yield Must return a scope on the model +name+, which will then be presented.
|
33
36
|
# @return [Hash] A hash of arrays of hashes. Top-level hash keys are pluralized model names, with values of arrays containing one hash per object that was found by the given given options.
|
34
37
|
def presenting(name, options = {}, &block)
|
35
38
|
options[:params] = HashWithIndifferentAccess.new(options[:params] || {})
|
39
|
+
check_for_old_options!(options)
|
40
|
+
set_default_filters_option!(options)
|
36
41
|
presented_class = (options[:model] || name)
|
37
42
|
presented_class = presented_class.classify.constantize if presented_class.is_a?(String)
|
38
43
|
scope = presented_class.instance_eval(&block)
|
39
44
|
count = 0
|
40
45
|
|
41
46
|
# grab the presenter that knows about filters and sorting etc.
|
42
|
-
options[:
|
47
|
+
options[:primary_presenter] ||= for!(presented_class)
|
43
48
|
|
44
49
|
# table name will be used to query the database for the filtered data
|
45
50
|
options[:table_name] = presented_class.table_name
|
46
51
|
|
47
|
-
|
48
|
-
options[:
|
49
|
-
|
50
|
-
allowed_includes = calculate_allowed_includes options[:presenter], presented_class, options[:params][:only].present?
|
51
|
-
includes_hash = filter_includes options[:params][:include], allowed_includes
|
52
|
-
|
53
|
-
if searching? options
|
54
|
-
# Search
|
55
|
-
sort_name, direction = calculate_sort_name_and_direction options
|
56
|
-
scope, count, ordered_search_ids = run_search(scope, includes_hash.keys.map(&:to_s), sort_name, direction, options)
|
57
|
-
else
|
58
|
-
# Filter
|
59
|
-
scope = run_filters scope, options
|
60
|
-
|
61
|
-
if options[:params][:only].present?
|
62
|
-
# Handle Only
|
63
|
-
scope, count = handle_only(scope, options[:params][:only])
|
64
|
-
else
|
65
|
-
# Paginate
|
66
|
-
scope, count = paginate scope, options
|
67
|
-
end
|
68
|
-
|
69
|
-
count = count.keys.length if count.is_a?(Hash)
|
70
|
-
|
71
|
-
# Ordering
|
72
|
-
scope = handle_ordering scope, options
|
73
|
-
end
|
52
|
+
options[:default_per_page] = default_per_page
|
53
|
+
options[:default_max_per_page] = default_max_per_page
|
54
|
+
options[:default_max_filter_and_search_page] = default_max_filter_and_search_page
|
74
55
|
|
75
|
-
|
76
|
-
records = scope.to_a
|
56
|
+
primary_models, count = strategy(options, scope).execute(scope)
|
77
57
|
|
78
58
|
# Determine if an exception should be raised on an empty result set.
|
79
|
-
if options[:raise_on_empty] &&
|
59
|
+
if options[:raise_on_empty] && primary_models.empty?
|
80
60
|
raise options[:empty_error_class] || ActiveRecord::RecordNotFound
|
81
61
|
end
|
82
62
|
|
83
|
-
|
84
|
-
|
63
|
+
structure_response(presented_class, primary_models, count, options)
|
64
|
+
end
|
85
65
|
|
86
|
-
|
87
|
-
|
88
|
-
|
66
|
+
def structure_response(presented_class, primary_models, count, options)
|
67
|
+
# key these models will use in the struct that is output
|
68
|
+
brainstem_key = brainstem_key_for!(presented_class)
|
89
69
|
|
90
|
-
|
91
|
-
|
92
|
-
models.uniq!
|
70
|
+
# filter the incoming :includes list by those available from this Presenter in the current context
|
71
|
+
selected_associations = filter_includes(options)
|
93
72
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
73
|
+
optional_fields = filter_optional_fields(options)
|
74
|
+
|
75
|
+
struct = { 'count' => count, brainstem_key => {}, 'results' => [] }
|
76
|
+
|
77
|
+
# Build top-level keys for all requested associations.
|
78
|
+
selected_associations.each do |association|
|
79
|
+
struct[brainstem_key_for!(association.target_class)] ||= {} unless association.polymorphic?
|
101
80
|
end
|
102
81
|
|
103
82
|
if primary_models.length > 0
|
104
|
-
|
105
|
-
|
106
|
-
|
83
|
+
associated_models = {}
|
84
|
+
presented_primary_models = options[:primary_presenter].group_present(primary_models,
|
85
|
+
selected_associations.map(&:name),
|
86
|
+
optional_fields: optional_fields,
|
87
|
+
load_associations_into: associated_models)
|
88
|
+
|
89
|
+
struct[brainstem_key] = presented_primary_models.each.with_object({}) { |model, obj| obj[model['id']] = model }
|
90
|
+
struct['results'] = presented_primary_models.map { |model| { 'key' => brainstem_key, 'id' => model['id'] } }
|
91
|
+
|
92
|
+
associated_models.each do |association_brainstem_key, associated_models_hash|
|
93
|
+
presenter = for!(associated_models_hash.values.first.class)
|
94
|
+
struct[association_brainstem_key] ||= {}
|
95
|
+
presenter.group_present(associated_models_hash.values).each do |model|
|
96
|
+
struct[association_brainstem_key][model['id']] ||= model
|
97
|
+
end
|
98
|
+
end
|
107
99
|
end
|
108
100
|
|
109
|
-
rewrite_keys_as_objects!(struct)
|
110
|
-
|
111
101
|
struct
|
112
102
|
end
|
113
103
|
|
@@ -120,271 +110,77 @@ module Brainstem
|
|
120
110
|
# @param [*Class] klasses One or more classes that can be presented by +presenter_class+.
|
121
111
|
def add_presenter_class(presenter_class, *klasses)
|
122
112
|
klasses.each do |klass|
|
123
|
-
presenters[klass.to_s] = presenter_class
|
113
|
+
presenters[klass.to_s] = presenter_class
|
124
114
|
end
|
125
115
|
end
|
126
116
|
|
127
|
-
# @return [Brainstem::Presenter, nil]
|
117
|
+
# @return [Brainstem::Presenter, nil] A new instance of the Presenter that knows how to present the class +klass+, or +nil+ if there isn't one.
|
128
118
|
def for(klass)
|
129
|
-
presenters[klass.to_s]
|
119
|
+
presenters[klass.to_s].try(:new)
|
130
120
|
end
|
131
121
|
|
132
|
-
# @return [Brainstem::Presenter]
|
133
|
-
# @raise [ArgumentError] if there is no known
|
122
|
+
# @return [Brainstem::Presenter] A new instance of the Presenter that knows how to present the class +klass+.
|
123
|
+
# @raise [ArgumentError] if there is no known Presenter for +klass+.
|
134
124
|
def for!(klass)
|
135
125
|
self.for(klass) || raise(ArgumentError, "Unable to find a presenter for class #{klass}")
|
136
126
|
end
|
137
127
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
limit = calculate_limit(options)
|
143
|
-
offset = calculate_offset(options)
|
144
|
-
else
|
145
|
-
limit = calculate_per_page(options)
|
146
|
-
offset = limit * (calculate_page(options) - 1)
|
147
|
-
end
|
148
|
-
|
149
|
-
[scope.limit(limit).offset(offset).uniq, scope.select("distinct #{scope.connection.quote_table_name options[:table_name]}.id").count] # as of Rails 3.2.5, uniq.count generates the wrong SQL.
|
150
|
-
end
|
151
|
-
|
152
|
-
def calculate_per_page(options)
|
153
|
-
per_page = [(options[:params][:per_page] || options[:per_page] || default_per_page).to_i, (options[:max_per_page] || default_max_per_page).to_i].min
|
154
|
-
per_page = default_per_page if per_page < 1
|
155
|
-
per_page
|
156
|
-
end
|
157
|
-
|
158
|
-
def calculate_page(options)
|
159
|
-
[(options[:params][:page] || 1).to_i, 1].max
|
160
|
-
end
|
161
|
-
|
162
|
-
def calculate_limit(options)
|
163
|
-
[[options[:params][:limit].to_i, 1].max, default_max_per_page].min
|
164
|
-
end
|
165
|
-
|
166
|
-
def calculate_offset(options)
|
167
|
-
[options[:params][:offset].to_i, 0].max
|
168
|
-
end
|
169
|
-
|
170
|
-
# Gather allowed includes by inspecting the presented hash. For now, this requires that a new instance of the
|
171
|
-
# presented class always be presentable.
|
172
|
-
def calculate_allowed_includes(presenter, presented_class, is_only_query)
|
173
|
-
allowed_includes = {}
|
174
|
-
model = presented_class.new
|
175
|
-
reflections = Brainstem::PresenterCollection.reflections(model.class)
|
176
|
-
presenter.present(model).each do |k, v|
|
177
|
-
next unless v.is_a?(AssociationField)
|
178
|
-
next if v.restrict_to_only && !is_only_query
|
179
|
-
|
180
|
-
if v.json_name
|
181
|
-
v.json_name = v.json_name.tableize.to_sym
|
182
|
-
else
|
183
|
-
association = reflections[v.method_name.to_s]
|
184
|
-
if association && !association.options[:polymorphic]
|
185
|
-
v.json_name = association && association.table_name.to_sym
|
186
|
-
if v.json_name.nil?
|
187
|
-
raise ":json_name is a required option for method-based associations (#{presented_class}##{v.method_name})"
|
188
|
-
end
|
189
|
-
end
|
190
|
-
end
|
191
|
-
allowed_includes[k.to_s] = v
|
192
|
-
end
|
193
|
-
allowed_includes
|
194
|
-
end
|
195
|
-
|
196
|
-
def filter_includes(user_includes, allowed_includes)
|
197
|
-
filtered_includes = {}
|
198
|
-
|
199
|
-
(user_includes || '').split(',').each do |k|
|
200
|
-
allowed = allowed_includes[k]
|
201
|
-
if allowed
|
202
|
-
filtered_includes[k] = allowed
|
203
|
-
end
|
204
|
-
end
|
205
|
-
filtered_includes
|
206
|
-
end
|
207
|
-
|
208
|
-
def handle_only(scope, only)
|
209
|
-
ids = (only || "").split(",").select {|id| id =~ /\A\d+\Z/}.uniq
|
210
|
-
[scope.where(:id => ids), scope.where(:id => ids).count]
|
128
|
+
def brainstem_key_for!(klass)
|
129
|
+
presenter = presenters[klass.to_s]
|
130
|
+
raise(ArgumentError, "Unable to find a presenter for class #{klass}") unless presenter
|
131
|
+
presenter.configuration[:brainstem_key] || klass.table_name
|
211
132
|
end
|
212
133
|
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
else
|
221
|
-
scope = scope.send(filter_name, arg)
|
134
|
+
# @raise [StandardError] if any presenter in this collection is invalid.
|
135
|
+
def validate!
|
136
|
+
errors = []
|
137
|
+
presenters.each do |name, klass|
|
138
|
+
validator = Brainstem::PresenterValidator.new(klass)
|
139
|
+
unless validator.valid?
|
140
|
+
errors += validator.errors.full_messages.map { |error| "#{name}: #{error}" }
|
222
141
|
end
|
223
142
|
end
|
224
|
-
|
225
|
-
scope
|
143
|
+
raise "PresenterCollection invalid:\n - #{errors.join("\n - ")}" if errors.length > 0
|
226
144
|
end
|
227
145
|
|
228
|
-
|
229
|
-
filters_hash = {}
|
230
|
-
run_defaults = options.has_key?(:apply_default_filters) ? options[:apply_default_filters] : true
|
231
|
-
|
232
|
-
(options[:presenter].filters || {}).each do |filter_name, filter|
|
233
|
-
requested = options[:params][filter_name]
|
234
|
-
requested = requested.is_a?(Array) ? requested : (requested.present? ? requested.to_s : nil)
|
235
|
-
requested = requested == "true" ? true : (requested == "false" ? false : requested)
|
146
|
+
private
|
236
147
|
|
237
|
-
|
238
|
-
|
239
|
-
filters_hash[filter_name] = args unless args.nil?
|
240
|
-
end
|
148
|
+
def strategy(options, scope)
|
149
|
+
strat = options[:primary_presenter].get_query_strategy
|
241
150
|
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
# Runs the current search_block and returns an array of [scope of the resulting ids, result count, result ids]
|
246
|
-
# If the search_block returns a falsy value a SearchUnavailableError is raised.
|
247
|
-
# Your search block should return a list of ids and the count of ids found, or false if search is unavailable.
|
248
|
-
def run_search(scope, includes, sort_name, direction, options)
|
249
|
-
return scope unless searching? options
|
250
|
-
|
251
|
-
search_options = HashWithIndifferentAccess.new(
|
252
|
-
:include => includes,
|
253
|
-
:order => { :sort_order => sort_name, :direction => direction },
|
254
|
-
)
|
255
|
-
|
256
|
-
if options[:params][:limit].present? && options[:params][:offset].present?
|
257
|
-
search_options[:limit] = calculate_limit(options)
|
258
|
-
search_options[:offset] = calculate_offset(options)
|
259
|
-
else
|
260
|
-
search_options[:per_page] = calculate_per_page(options)
|
261
|
-
search_options[:page] = calculate_page(options)
|
262
|
-
end
|
263
|
-
|
264
|
-
search_options.reverse_merge!(extract_filters(options))
|
265
|
-
|
266
|
-
result_ids, count = options[:presenter].search_block.call(options[:params][:search], search_options)
|
267
|
-
if result_ids
|
268
|
-
[scope.where(:id => result_ids ), count, result_ids]
|
269
|
-
else
|
270
|
-
raise(SearchUnavailableError, 'Search is currently unavailable')
|
271
|
-
end
|
151
|
+
return Brainstem::QueryStrategies::FilterAndSearch.new(options) if strat == :filter_and_search && searching?(options)
|
152
|
+
return Brainstem::QueryStrategies::FilterOrSearch.new(options)
|
272
153
|
end
|
273
154
|
|
274
155
|
def searching?(options)
|
275
|
-
options[:params][:search] && options[:
|
276
|
-
end
|
277
|
-
|
278
|
-
def order_for_search(records, ordered_search_ids)
|
279
|
-
ids_to_position = {}
|
280
|
-
ordered_records = []
|
281
|
-
|
282
|
-
ordered_search_ids.each_with_index do |id, index|
|
283
|
-
ids_to_position[id] = index
|
284
|
-
end
|
285
|
-
|
286
|
-
records.each do |record|
|
287
|
-
ordered_records[ids_to_position[record.id]] = record
|
288
|
-
end
|
289
|
-
|
290
|
-
ordered_records
|
291
|
-
end
|
292
|
-
|
293
|
-
def handle_ordering(scope, options)
|
294
|
-
order, direction = calculate_order_and_direction(options)
|
295
|
-
|
296
|
-
case order
|
297
|
-
when Proc
|
298
|
-
order.call(scope, direction)
|
299
|
-
when nil
|
300
|
-
scope
|
301
|
-
else
|
302
|
-
scope.order(order.to_s + " " + direction)
|
303
|
-
end
|
304
|
-
end
|
305
|
-
|
306
|
-
def calculate_order_and_direction(options)
|
307
|
-
sort_name, direction = calculate_sort_name_and_direction(options)
|
308
|
-
sort_orders = (options[:presenter].sort_orders || {})
|
309
|
-
order = sort_orders[sort_name]
|
310
|
-
|
311
|
-
[order, direction]
|
156
|
+
options[:params][:search] && options[:primary_presenter].configuration[:search].present?
|
312
157
|
end
|
313
158
|
|
314
|
-
def
|
315
|
-
|
316
|
-
sort_name, direction = (options[:params][:order] || "").split(":")
|
317
|
-
sort_orders = options[:presenter].sort_orders || {}
|
318
|
-
unless sort_name.present? && sort_orders[sort_name]
|
319
|
-
sort_name = default_column
|
320
|
-
direction = default_direction
|
321
|
-
end
|
159
|
+
def filter_includes(options)
|
160
|
+
allowed_associations = options[:primary_presenter].allowed_associations(options[:params][:only].present?)
|
322
161
|
|
323
|
-
[
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
records.tap do |models|
|
328
|
-
association_names_to_preload = includes_hash.values.map {|i| i.method_name }
|
329
|
-
if models.first
|
330
|
-
reflections = Brainstem::PresenterCollection.reflections(models.first.class)
|
331
|
-
association_names_to_preload.reject! { |association| !reflections.has_key?(association.to_s) }
|
332
|
-
end
|
333
|
-
if association_names_to_preload.any?
|
334
|
-
Brainstem::PresenterCollection.preload(models, association_names_to_preload)
|
335
|
-
Brainstem.logger.info "Eager loaded #{association_names_to_preload.join(", ")}."
|
336
|
-
end
|
337
|
-
end
|
338
|
-
end
|
339
|
-
|
340
|
-
def gather_associations(models, includes_hash)
|
341
|
-
record_hash = {}
|
342
|
-
primary_models = []
|
343
|
-
|
344
|
-
includes_hash.each do |include, include_data|
|
345
|
-
record_hash[include_data.json_name] ||= [] if include_data.json_name
|
346
|
-
end
|
347
|
-
|
348
|
-
models.each do |model|
|
349
|
-
primary_models << model
|
350
|
-
|
351
|
-
includes_hash.each do |include, include_data|
|
352
|
-
models = Array(include_data.call(model))
|
353
|
-
|
354
|
-
if include_data.json_name
|
355
|
-
record_hash[include_data.json_name] += models
|
356
|
-
else
|
357
|
-
# polymorphic associations' tables must be figured out now
|
358
|
-
models.each do |record|
|
359
|
-
json_name = record.class.table_name.to_sym
|
360
|
-
record_hash[json_name] ||= []
|
361
|
-
record_hash[json_name] << record
|
362
|
-
end
|
162
|
+
[].tap do |selected_associations|
|
163
|
+
(options[:params][:include] || '').split(',').each do |k|
|
164
|
+
if association = allowed_associations[k]
|
165
|
+
selected_associations << association
|
363
166
|
end
|
364
167
|
end
|
365
168
|
end
|
366
|
-
|
367
|
-
[primary_models, record_hash]
|
368
169
|
end
|
369
170
|
|
370
|
-
def
|
371
|
-
(
|
372
|
-
struct[key] = struct[key].inject({}) {|memo, obj| memo[obj[:id] || obj["id"] || "unknown_id"] = obj; memo }
|
373
|
-
end
|
171
|
+
def filter_optional_fields(options)
|
172
|
+
options[:params][:optional_fields].to_s.split(',').map(&:strip) & options[:primary_presenter].configuration[:fields].keys
|
374
173
|
end
|
375
174
|
|
376
|
-
|
175
|
+
def set_default_filters_option!(options)
|
176
|
+
return unless options[:params].has_key?(:apply_default_filters)
|
377
177
|
|
378
|
-
|
379
|
-
def self.reflections(klass)
|
380
|
-
klass.reflections.each_with_object({}) { |(key, value), memo| memo[key.to_s] = value }
|
178
|
+
options[:apply_default_filters] = [true, "true", "TRUE", 1, "1"].include? options[:params].delete(:apply_default_filters)
|
381
179
|
end
|
382
180
|
|
383
|
-
def
|
384
|
-
if
|
385
|
-
|
386
|
-
else
|
387
|
-
ActiveRecord::Associations::Preloader.new(models, association_names).run
|
181
|
+
def check_for_old_options!(options)
|
182
|
+
if options[:as].present?
|
183
|
+
raise "PresenterCollection#presenting no longer accepts the :as option. Use the brainstem_key annotation in your presenters instead."
|
388
184
|
end
|
389
185
|
end
|
390
186
|
end
|