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
Indicates which models are to be presented by this presenter.
-
.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 ⇒ Object
Save all of the presentables, wrapped in a transaction.
-
#save! ⇒ 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
90 91 92 93 94 95 96 97 98 |
# File 'lib/active_presenter/base.rb', line 90 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.
134 135 136 |
# File 'lib/active_presenter/base.rb', line 134 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.
11 12 13 |
# File 'lib/active_presenter/base.rb', line 11 def errors @errors end |
Class Method Details
.human_attribute_name(attribute_key_name, options = {}) ⇒ Object
47 48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/active_presenter/base.rb', line 47 def self.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:
70 71 72 73 74 75 76 |
# File 'lib/active_presenter/base.rb', line 70 def self.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
Indicates which models are to be presented by this presenter. i.e.
class SignupPresenter < ActivePresenter::Base
presents :user, :account
end
In the above example, :user will (predictably) become User. If you want to override this behaviour, specify the desired types in a hash, as so:
class PresenterWithTwoAddresses < ActivePresenter::Base
presents :primary_address => Address, :secondary_address => Address
end
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
# File 'lib/active_presenter/base.rb', line 31 def self.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 # We must reassign in derrived classes rather than mutating the attribute in Base self.presented = self.presented.merge(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
66 67 68 |
# File 'lib/active_presenter/base.rb', line 66 def self.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”)
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
# File 'lib/active_presenter/base.rb', line 104 def attributes=(attrs) return if attrs.nil? attrs = attrs.stringify_keys multi_parameter_attributes = {} attrs = sanitize_for_mass_assignment(attrs) 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?
155 156 157 |
# File 'lib/active_presenter/base.rb', line 155 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
218 219 220 |
# File 'lib/active_presenter/base.rb', line 218 def id # :nodoc: nil end |
#new_record? ⇒ Boolean
222 223 224 |
# File 'lib/active_presenter/base.rb', line 222 def new_record? presented_instances.map(&:new_record?).all? end |
#persisted? ⇒ Boolean
226 227 228 |
# File 'lib/active_presenter/base.rb', line 226 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.
128 129 130 |
# File 'lib/active_presenter/base.rb', line 128 def respond_to?(method, include_private = false) presented_attribute?(method) || super end |
#save ⇒ Object
Save all of the presentables, wrapped in a transaction.
Returns true or false based on success.
163 164 165 166 167 168 169 170 171 172 173 174 |
# File 'lib/active_presenter/base.rb', line 163 def save saved = false ActiveRecord::Base.transaction do if 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! ⇒ Object
Save all of the presentables wrapped in a transaction.
Returns true on success, will raise otherwise.
180 181 182 183 184 185 186 187 188 189 190 191 |
# File 'lib/active_presenter/base.rb', line 180 def save! saved = false ActiveRecord::Base.transaction do raise ActiveRecord::RecordInvalid.new(self) unless 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>)
213 214 215 |
# File 'lib/active_presenter/base.rb', line 213 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.
197 198 199 200 |
# File 'lib/active_presenter/base.rb', line 197 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.
140 141 142 143 144 145 146 147 148 149 150 151 152 |
# File 'lib/active_presenter/base.rb', line 140 def valid? validated = false errors.clear result = _run_validation_callbacks do presented.keys.each do |type| presented_inst = send(type) next unless save?(type, presented_inst) merge_errors(presented_inst, type) unless presented_inst.valid? end validated = true end errors.empty? && validated end |