brainstem 0.2.6.1 → 1.0.0.pre.1
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.
- 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
|