Class: ActivePresenter::Base
- Inherits:
-
Object
- Object
- ActivePresenter::Base
- Extended by:
- ActiveModel::Callbacks, ActiveModel::Naming, ActiveModel::Translation
- Includes:
- ActiveModel::Conversion, ActiveModel::MassAssignmentSecurity
- Defined in:
- lib/active_presenter/base.rb
Overview
Base class for presenters. See README for usage.
Instance Attribute Summary collapse
-
#errors ⇒ Object
readonly
Returns the value of attribute errors.
Class Method Summary collapse
- .human_attribute_name(attribute_key_name, options = {}) ⇒ Object
-
.human_name(options = {}) ⇒ Object
:nodoc:.
- .presents(*types) ⇒ Object
-
.self_and_descendants_from_active_record ⇒ Object
Since ActivePresenter does not descend from ActiveRecord, we need to mimic some ActiveRecord behavior in order for the ActiveRecord::Errors object we’re using to work properly.
Instance Method Summary collapse
-
#attributes=(attrs) ⇒ Object
Set the attributes of the presentable instances using the type_attribute form (i.e. user_login => ‘james’), or the multiparameter attribute form (i.e. => “1980”, user_birthday(2i) => “3”).
-
#changed? ⇒ Boolean
Do any of the attributes have unsaved changes?.
-
#id ⇒ Object
We define #id and #new_record? to play nice with form_for(@presenter) in Rails.
-
#initialize(args = {}) ⇒ Base
constructor
Accepts arguments in two forms.
-
#method_missing(method_name, *args, &block) ⇒ Object
Handles the decision about whether to delegate getters and setters to presentable instances.
- #new_record? ⇒ Boolean
- #persisted? ⇒ Boolean
-
#respond_to?(method, include_private = false) ⇒ Boolean
Makes sure that the presenter is accurate about responding to presentable’s attributes, even though they are handled by method_missing.
-
#save(options = {}) ⇒ Object
Save all of the presentables, wrapped in a transaction.
-
#save!(options = {}) ⇒ Object
Save all of the presentables wrapped in a transaction.
-
#save?(presented_key, presented_instance) ⇒ Boolean
Should this presented instance be saved? By default, this returns true Called from #save and #save!.
-
#update_attributes(attrs) ⇒ Object
Update attributes, and save the presentables.
-
#valid? ⇒ Boolean
Returns boolean based on the validity of the presentables by calling valid? on each of them.
Constructor Details
#initialize(args = {}) ⇒ Base
Accepts arguments in two forms. For example, if you had a SignupPresenter that presented User, and Account, you could specify arguments in the following two forms:
1. SignupPresenter.new(:user_login => 'james', :user_password => 'swordfish', :user_password_confirmation => 'swordfish', :account_subdomain => 'giraffesoft')
- This form is useful for initializing a new presenter from the params hash: i.e. SignupPresenter.new(params[:signup_presenter])
2. SignupPresenter.new(:user => User.find(1), :account => Account.find(2))
- This form is useful if you have instances that you'd like to edit using the presenter. You can subsequently call presenter.update_attributes(params[:signup_presenter]) just like with a regular AR instance.
Both forms can also be mixed together: SignupPresenter.new(:user => User.find(1), :user_login => ‘james’)
In this case, the login attribute will be updated on the user instance provided.
If you don’t specify an instance, one will be created by calling Model.new
92 93 94 95 96 97 98 99 100 |
# File 'lib/active_presenter/base.rb', line 92 def initialize(args = {}) @errors = ActiveModel::Errors.new(self) return self unless args presented.each do |type, klass| value = args.delete(type) send("#{type}=", value.is_a?(klass) ? value : klass.new) end self.attributes = args end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method_name, *args, &block) ⇒ Object
Handles the decision about whether to delegate getters and setters to presentable instances.
135 136 137 |
# File 'lib/active_presenter/base.rb', line 135 def method_missing(method_name, *args, &block) presented_attribute?(method_name) ? (method_name, *args, &block) : super end |
Instance Attribute Details
#errors ⇒ Object (readonly)
Returns the value of attribute errors.
12 13 14 |
# File 'lib/active_presenter/base.rb', line 12 def errors @errors end |
Class Method Details
.human_attribute_name(attribute_key_name, options = {}) ⇒ Object
48 49 50 51 52 53 54 55 56 57 58 59 |
# File 'lib/active_presenter/base.rb', line 48 def human_attribute_name(attribute_key_name, = {}) presentable_type = presented.keys.detect do |type| attribute_key_name.to_s.starts_with?("#{type}_") || attribute_key_name.to_s == type.to_s end attribute_key_name_without_class = attribute_key_name.to_s.gsub("#{presentable_type}_", "") if presented[presentable_type] and attribute_key_name_without_class != presentable_type.to_s presented[presentable_type].human_attribute_name(attribute_key_name_without_class, ) else I18n.translate(presentable_type, .merge(:default => presentable_type.to_s.humanize, :scope => [:activerecord, :models])) end end |
.human_name(options = {}) ⇒ Object
:nodoc:
71 72 73 74 75 76 77 |
# File 'lib/active_presenter/base.rb', line 71 def human_name( = {}) # :nodoc: defaults = self_and_descendants_from_active_record.map do |klass| :"#{klass.name.underscore}" end defaults << self.name.humanize I18n.translate(defaults.shift, {:scope => [:activerecord, :models], :count => 1, :default => defaults}.merge()) end |
.presents(*types) ⇒ Object
33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/active_presenter/base.rb', line 33 def presents(*types) types_and_classes = types. types.each { |t| types_and_classes[t] = t.to_s.tableize.classify.constantize } attr_accessor *types_and_classes.keys types_and_classes.keys.each do |t| define_method("#{t}_errors") do send(t).errors end presented[t] = types_and_classes[t] end end |
.self_and_descendants_from_active_record ⇒ Object
Since ActivePresenter does not descend from ActiveRecord, we need to mimic some ActiveRecord behavior in order for the ActiveRecord::Errors object we’re using to work properly.
This problem was introduced with Rails 2.3.4. Fix courtesy gist.github.com/191263
67 68 69 |
# File 'lib/active_presenter/base.rb', line 67 def self_and_descendants_from_active_record # :nodoc: [self] end |
Instance Method Details
#attributes=(attrs) ⇒ Object
Set the attributes of the presentable instances using the type_attribute form (i.e. user_login => ‘james’), or the multiparameter attribute form (i.e. => “1980”, user_birthday(2i) => “3”)
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
# File 'lib/active_presenter/base.rb', line 106 def attributes=(attrs) return if attrs.nil? attrs = attrs.stringify_keys multi_parameter_attributes = {} sanitize_for_mass_assignment(attrs).each do |k,v| if (base_attribute = k.to_s.split("(").first) != k.to_s presentable = presentable_for(base_attribute) multi_parameter_attributes[presentable] ||= {} multi_parameter_attributes[presentable].merge!(flatten_attribute_name(k,presentable).to_sym => v) else send("#{k}=", v) unless attribute_protected?(k) end end multi_parameter_attributes.each do |presentable,multi_attrs| send(presentable).send(:attributes=, multi_attrs) end end |
#changed? ⇒ Boolean
Do any of the attributes have unsaved changes?
156 157 158 |
# File 'lib/active_presenter/base.rb', line 156 def changed? presented_instances.map(&:changed?).any? end |
#id ⇒ Object
We define #id and #new_record? to play nice with form_for(@presenter) in Rails
219 220 221 |
# File 'lib/active_presenter/base.rb', line 219 def id # :nodoc: nil end |
#new_record? ⇒ Boolean
223 224 225 |
# File 'lib/active_presenter/base.rb', line 223 def new_record? presented_instances.map(&:new_record?).all? end |
#persisted? ⇒ Boolean
227 228 229 |
# File 'lib/active_presenter/base.rb', line 227 def persisted? presented_instances.map(&:persisted?).all? end |
#respond_to?(method, include_private = false) ⇒ Boolean
Makes sure that the presenter is accurate about responding to presentable’s attributes, even though they are handled by method_missing.
129 130 131 |
# File 'lib/active_presenter/base.rb', line 129 def respond_to?(method, include_private = false) presented_attribute?(method) || super end |
#save(options = {}) ⇒ Object
Save all of the presentables, wrapped in a transaction.
Returns true or false based on success.
164 165 166 167 168 169 170 171 172 173 174 175 |
# File 'lib/active_presenter/base.rb', line 164 def save(={}) saved = false ActiveRecord::Base.transaction do if !perform_validations?() || (perform_validations?() && valid?) _run_save_callbacks do saved = presented.keys.select {|key| save?(key, send(key))}.all? {|key| send(key).save} raise ActiveRecord::Rollback unless saved end end end saved end |
#save!(options = {}) ⇒ Object
Save all of the presentables wrapped in a transaction.
Returns true on success, will raise otherwise.
181 182 183 184 185 186 187 188 189 190 191 192 |
# File 'lib/active_presenter/base.rb', line 181 def save!(={}) saved = false ActiveRecord::Base.transaction do raise ActiveRecord::RecordInvalid.new(self) if perform_validations?() && !valid? _run_save_callbacks do presented.keys.select {|key| save?(key, send(key))}.all? {|key| send(key).save!} saved = true end raise ActiveRecord::RecordNotSaved.new(self) unless saved end saved end |
#save?(presented_key, presented_instance) ⇒ Boolean
Should this presented instance be saved? By default, this returns true Called from #save and #save!
For
class SignupPresenter < ActivePresenter::Base
presents :account, :user
end
#save? will be called twice:
save?(:account, #<Account:0x1234dead>)
save?(:user, #<User:0xdeadbeef>)
214 215 216 |
# File 'lib/active_presenter/base.rb', line 214 def save?(presented_key, presented_instance) true end |
#update_attributes(attrs) ⇒ Object
Update attributes, and save the presentables
Returns true or false based on success.
198 199 200 201 |
# File 'lib/active_presenter/base.rb', line 198 def update_attributes(attrs) self.attributes = attrs save end |
#valid? ⇒ Boolean
Returns boolean based on the validity of the presentables by calling valid? on each of them.
141 142 143 144 145 146 147 148 149 150 151 152 153 |
# File 'lib/active_presenter/base.rb', line 141 def valid? validated = false errors.clear result = _run_validation_callbacks do presented.each do |type, klass| presented_inst = (send(type) || klass.new) next unless save?(type, presented_inst) merge_errors(presented_inst, type) unless presented_inst.valid? end validated = true end errors.empty? && validated end |