Class: Brainstem::Presenter Abstract
- Inherits:
-
Object
- Object
- Brainstem::Presenter
- Includes:
- Concerns::PresenterDSL
- Defined in:
- lib/brainstem/presenter.rb,
lib/brainstem/time_classes.rb
Overview
Subclass and override #present to implement a presenter.
Constant Summary collapse
- TIME_CLASSES =
This constant stores an array of classes that we will treat as times. Unfortunately, ActiveSupport::TimeWithZone does not descend from Time, so we put them into this array for later use.
[Time]
Class Method Summary collapse
- .merged_helper_class ⇒ Object
-
.namespace ⇒ String
Return the second-to-last module in the name of this presenter, which Brainstem considers to be the ‘namespace’.
-
.possible_brainstem_keys ⇒ Object
Returns the set of possible brainstem keys for the classes presented.
-
.presents(*klasses) ⇒ Object
Accepts a list of classes that this specific presenter knows how to present.
-
.reflections(klass) ⇒ Object
In Rails 4.2, ActiveRecord::Base#reflections started being keyed by strings instead of symbols.
- .reset! ⇒ Object
Instance Method Summary collapse
-
#allowed_associations(is_only_query) ⇒ Hash
private
Determines which associations are valid for inclusion in the current context.
-
#apply_filters_to_scope(scope, user_params, options) ⇒ Object
Given user params, build a hash of validated filter names to their unsanitized arguments.
-
#apply_ordering_to_scope(scope, user_params) ⇒ Object
Given user params, apply a validated sort order to the given scope.
-
#calculate_sort_name_and_direction(user_params = {}) ⇒ Object
Clean and validate a sort order and direction from user params.
-
#custom_preload(models, requested_associations = []) ⇒ Object
Subclasses can define this if they wish.
-
#extract_filters(user_params, options = {}) ⇒ Object
Given user params, build a hash of validated filter names to their unsanitized arguments.
- #get_query_strategy ⇒ Object
-
#group_present(models, requested_associations = [], options = {}) ⇒ Object
Calls #custom_preload and then presents all models.
- #present(model) ⇒ Object deprecated Deprecated.
- #present_model(model, requested_associations = [], options = {}) ⇒ Object
-
#run_search(query, search_options) ⇒ Object
Execute the stored search block.
Methods included from Concerns::InheritableConfiguration
Class Method Details
.merged_helper_class ⇒ Object
48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/brainstem/presenter.rb', line 48 def self.merged_helper_class @helper_classes ||= {} @helper_classes[configuration[:helpers].to_a.map(&:object_id)] ||= begin Class.new.tap do |klass| (configuration[:helpers] || []).each do |helper| klass.send :include, helper end end end end |
.namespace ⇒ String
Return the second-to-last module in the name of this presenter, which Brainstem considers to be the ‘namespace’. E.g., Api::V1::FooPresenter has a namespace of “V1”.
44 45 46 |
# File 'lib/brainstem/presenter.rb', line 44 def self.namespace self.to_s.split("::")[-2].try(:downcase) end |
.possible_brainstem_keys ⇒ Object
Returns the set of possible brainstem keys for the classes presented.
If the presenter specifies a key, that will be returned as the only member of the set.
35 36 37 38 39 |
# File 'lib/brainstem/presenter.rb', line 35 def self.possible_brainstem_keys @possible_brainstem_keys ||= begin Set.new(presents.map(&presenter_collection.method(:brainstem_key_for!))) end end |
.presents(*klasses) ⇒ Object
Accepts a list of classes that this specific presenter knows how to present. These are not inherited.
17 18 19 20 21 22 23 24 25 26 27 |
# File 'lib/brainstem/presenter.rb', line 17 def self.presents(*klasses) @presents ||= [] if klasses.length > 0 if klasses.any? { |klass| klass.is_a?(String) || klass.is_a?(Symbol) } raise "Brainstem Presenter#presents now expects a Class instead of a class name" end @presents.concat(klasses).uniq! Brainstem.add_presenter_class(self, namespace, *klasses) end @presents end |
.reflections(klass) ⇒ Object
In Rails 4.2, ActiveRecord::Base#reflections started being keyed by strings instead of symbols.
66 67 68 |
# File 'lib/brainstem/presenter.rb', line 66 def self.reflections(klass) klass.reflections.each_with_object({}) { |(key, value), memo| memo[key.to_s] = value } end |
.reset! ⇒ Object
60 61 62 63 |
# File 'lib/brainstem/presenter.rb', line 60 def self.reset! clear_configuration! @helper_classes = @presents = nil end |
Instance Method Details
#allowed_associations(is_only_query) ⇒ Hash
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Determines which associations are valid for inclusion in the current context. Mostly just removes only-restricted associations when needed.
139 140 141 142 143 144 145 |
# File 'lib/brainstem/presenter.rb', line 139 def allowed_associations(is_only_query) ActiveSupport::HashWithIndifferentAccess.new.tap do |associations| configuration[:associations].each do |name, association| associations[name] = association unless association.[:restrict_to_only] && !is_only_query end end end |
#apply_filters_to_scope(scope, user_params, options) ⇒ Object
Given user params, build a hash of validated filter names to their unsanitized arguments.
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 |
# File 'lib/brainstem/presenter.rb', line 186 def apply_filters_to_scope(scope, user_params, ) helper_instance = fresh_helper_instance requested_filters = extract_filters(user_params, ) requested_filters.each do |filter_name, filter_arg| filter_lambda = configuration[:filters][filter_name][:value] args_for_filter_lambda = [filter_arg] args_for_filter_lambda << requested_filters if configuration[:filters][filter_name][:include_params] if filter_lambda scope = helper_instance.instance_exec(scope, *args_for_filter_lambda, &filter_lambda) else scope = scope.send(filter_name, *args_for_filter_lambda) end end scope end |
#apply_ordering_to_scope(scope, user_params) ⇒ Object
Given user params, apply a validated sort order to the given scope.
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 |
# File 'lib/brainstem/presenter.rb', line 207 def apply_ordering_to_scope(scope, user_params) sort_name, direction = calculate_sort_name_and_direction(user_params) order = configuration[:sort_orders].fetch(sort_name, {})[:value] ordered_scope = case order when Proc fresh_helper_instance.instance_exec(scope, direction, &order) when nil scope else scope.reorder(order.to_s + " " + direction) end fallback_deterministic_sort = assemble_primary_key_sort(scope) # Chain on a tiebreaker sort to ensure deterministic ordering of multiple pages of data if fallback_deterministic_sort ordered_scope.order(fallback_deterministic_sort) else ordered_scope end end |
#calculate_sort_name_and_direction(user_params = {}) ⇒ Object
Clean and validate a sort order and direction from user params.
248 249 250 251 252 253 254 255 256 257 |
# File 'lib/brainstem/presenter.rb', line 248 def calculate_sort_name_and_direction(user_params = {}) default_column, default_direction = (configuration[:default_sort_order] || "updated_at:desc").split(":") sort_name, direction = user_params['order'].to_s.split(":") unless sort_name.present? && configuration[:sort_orders][sort_name] sort_name = default_column direction = default_direction end [sort_name, direction == 'desc' ? 'desc' : 'asc'] end |
#custom_preload(models, requested_associations = []) ⇒ Object
Subclasses can define this if they wish. This method will be called by #group_present.
148 149 |
# File 'lib/brainstem/presenter.rb', line 148 def custom_preload(models, requested_associations = []) end |
#extract_filters(user_params, options = {}) ⇒ Object
Given user params, build a hash of validated filter names to their unsanitized arguments.
152 153 154 155 156 157 158 159 160 161 162 163 164 165 |
# File 'lib/brainstem/presenter.rb', line 152 def extract_filters(user_params, = {}) filters_hash = {} apply_default_filters = .fetch(:apply_default_filters) { true } configuration[:filters].each do |filter_name, | user_value = format_filter_value(user_params[filter_name]) filter_arg = apply_default_filters && user_value.nil? ? [:default] : user_value filters_hash[filter_name] = filter_arg unless filter_arg.nil? end filters_hash end |
#get_query_strategy ⇒ Object
77 78 79 80 81 82 |
# File 'lib/brainstem/presenter.rb', line 77 def get_query_strategy if configuration.has_key? :query_strategy strat = configuration[:query_strategy] strat.respond_to?(:call) ? fresh_helper_instance.instance_exec(&strat) : strat end end |
#group_present(models, requested_associations = [], options = {}) ⇒ Object
Calls #custom_preload and then presents all models.
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
# File 'lib/brainstem/presenter.rb', line 88 def group_present(models, requested_associations = [], = {}) association_objects_by_name = requested_associations.each_with_object({}) do |assoc_name, memo| memo[assoc_name.to_s] = configuration[:associations][assoc_name] if configuration[:associations][assoc_name] end # It's slightly ugly, but more efficient if we pre-load everything we # need and pass it through. context = { conditional_cache: { request: {} }, fields: configuration[:fields], conditionals: configuration[:conditionals], associations: configuration[:associations], reflections: reflections_for_model(models.first), association_objects_by_name: association_objects_by_name, optional_fields: [:optional_fields] || [], models: models, lookup: empty_lookup_cache(configuration[:fields].keys, association_objects_by_name.keys) } sanitized_association_names = association_objects_by_name.values.map(&:method_name) preload_associations! models, sanitized_association_names, context[:reflections] # Legacy: Overridable for custom preload behavior. custom_preload(models, association_objects_by_name.keys) models.map do |model| context[:conditional_cache][:model] = {} context[:helper_instance] = fresh_helper_instance result = present_fields(model, context, context[:fields]) load_associations!(model, result, context, ) add_id!(model, result) datetimes_to_json(result) end end |
#present(model) ⇒ Object
73 74 75 |
# File 'lib/brainstem/presenter.rb', line 73 def present(model) raise "#present is now deprecated" end |
#present_model(model, requested_associations = [], options = {}) ⇒ Object
123 124 125 |
# File 'lib/brainstem/presenter.rb', line 123 def present_model(model, requested_associations = [], = {}) group_present([model], requested_associations, ).first end |
#run_search(query, search_options) ⇒ Object
Execute the stored search block
243 244 245 |
# File 'lib/brainstem/presenter.rb', line 243 def run_search(query, ) fresh_helper_instance.instance_exec(query, , &configuration[:search]) end |