class ActivePodio::Base
Attributes
attributes[RW]
Public Class Methods
collection(response)
click to toggle source
Returns a struct that includes:
-
all: A collection model instances
-
count: The number of returned records
-
total_count: The total number of records matching the given conditions
# File lib/podio/active_podio/base.rb, line 295 def collection(response) result = Struct.new(:all, :count, :total_count).new(response['items'], response['filtered'], response['total']) result.all.map! { |item| new(item, :values_from_api => true) } result end
delegate_to_hash(hash_name, *attribute_names)
click to toggle source
# File lib/podio/active_podio/base.rb, line 301 def delegate_to_hash(hash_name, *attribute_names) options = attribute_names.extract_options! options.reverse_merge!(:prefix => false, :setter => false) options.assert_valid_keys(:prefix, :setter) attribute_names.each do |attribute_name| hash_index = attribute_name.to_s.gsub(/[\?!]/, '') method_name = "#{options[:prefix] ? "#{hash_name}_" : ''}#{attribute_name}" self.send(:define_method, method_name) do self.send("#{hash_name}=", {}) unless self.send(hash_name) self.send(hash_name)[hash_index] end if options[:setter] self.send(:define_method, "#{method_name}=") do |value| self.send("#{hash_name}=", {}) unless self.send(hash_name) self.send(hash_name)[hash_index] = value end end end end
has_many(name, options = {})
click to toggle source
Wraps a collection of hashes from the API to a collection of the given model
# File lib/podio/active_podio/base.rb, line 257 def has_many(name, options = {}) self._associations = self._associations.merge({name => :has_many}) self.send(:define_method, name) do klass = klass_for_association(options) instances = self.instance_variable_get("@#{name}_has_many_instances") unless instances.present? property = options[:property] || name.to_sym if self[property].present? && self[property].respond_to?(:map) instances = self[property].map { |attributes| klass.new(attributes, :belongs_to => { :model => self }) } self.instance_variable_set("@#{name}_has_many_instances", instances) else instances = [] end end instances end self.send(:define_method, "#{name}?") do self.send(name).length > 0 end end
has_one(name, options = {})
click to toggle source
Wraps a single hash provided from the API in the given model
# File lib/podio/active_podio/base.rb, line 233 def has_one(name, options = {}) self._associations = self._associations.merge({name => :has_one}) self.send(:define_method, name) do klass = klass_for_association(options) instance = self.instance_variable_get("@#{name}_has_one_instance") unless instance.present? property = options[:property] || name.to_sym if self[property].present? instance = klass.new(self[property], :belongs_to => { :model => self, :as => property }) self.instance_variable_set("@#{name}_has_one_instance", instance) else instance = nil end end instance end self.send(:define_method, "clear_#{name}") do self.instance_variable_set("@#{name}_has_one_instance", nil) end end
klass_from_string(klass_name)
click to toggle source
# File lib/podio/active_podio/base.rb, line 325 def klass_from_string(klass_name) klass = klass_name.constantize rescue nil klass = "Podio::#{klass_name}".constantize unless klass.respond_to?(:ancestors) && klass.ancestors.include?(ActivePodio::Base) return klass end
list(response)
click to toggle source
Returns a simple collection model instances
# File lib/podio/active_podio/base.rb, line 286 def list(response) response.map! { |item| new(item, :values_from_api => true) } response end
member(response)
click to toggle source
Returns a single instance of the model
# File lib/podio/active_podio/base.rb, line 281 def member(response) new(response, :values_from_api => true) end
new(attributes = {}, options = {})
click to toggle source
# File lib/podio/active_podio/base.rb, line 17 def initialize(attributes = {}, options = {}) attributes = {} if attributes.blank? self.attributes = Hash[*self.valid_attributes.collect { |n| [n.to_sym, nil] }.flatten].merge(attributes.symbolize_keys) @values_from_api = options[:values_from_api] # Used to determine if date times should be converted from local to utc, or are already utc self.initialize_attributes(attributes) @belongs_to = options[:belongs_to] # Allows has_one associations to communicate their changed content back to their parent model @values_from_api = false end
output_attribute_as_json(*attributes)
click to toggle source
# File lib/podio/active_podio/base.rb, line 321 def output_attribute_as_json(*attributes) self.json_attributes += attributes end
property(name, type = :string, options = {})
click to toggle source
Defines the the supported attributes of the model
# File lib/podio/active_podio/base.rb, line 206 def property(name, type = :string, options = {}) self.valid_attributes += [name] case type when :datetime define_datetime_accessor(name, options) when :date define_date_accessor(name) when :time define_time_accessor(name) when :integer define_integer_accessor(name) when :boolean define_generic_accessor(name, :setter => false) define_boolean_accessors(name) when :array define_array_accessors(name) when :float define_float_accessor(name) when :decimal define_decimal_accessor(name) else define_generic_accessor(name) end end
Private Class Methods
define_array_accessors(name)
click to toggle source
# File lib/podio/active_podio/base.rb, line 487 def define_array_accessors(name) unless name.to_s == name.to_s.pluralize self.send(:define_method, name) do self[name.to_sym] || [] end self.send(:define_method, "#{name}=") do |array| self[name.to_sym] = array.respond_to?(:reject) ? array.reject(&:blank?) : array end end self.send(:define_method, name.to_s.pluralize) do self[name.to_sym] || [] end self.send(:define_method, "#{name.to_s.pluralize}=") do |array| self[name.to_sym] = array.respond_to?(:reject) ? array.reject(&:blank?) : array end self.send(:define_method, "default_#{name.to_s.singularize}") do self[name.to_sym].try(:first).presence end self.send(:define_method, "default_#{name.to_s.singularize}=") do |value| if self[name.to_sym].try(:first).present? self[name.to_sym][0] = value else self[name.to_sym] = [value] end self[name.to_sym].compact! end end
define_boolean_accessors(name)
click to toggle source
# File lib/podio/active_podio/base.rb, line 467 def define_boolean_accessors(name) self.send(:define_method, "#{name}?") do if self[name.to_sym].present? %w{ true 1 yes }.include?(self[name.to_sym].to_s.strip.downcase) else nil end end self.send(:define_method, "#{name}=") do |value| if value == true || value == false self[name.to_sym] = value elsif value.present? self[name.to_sym] = %w{ true 1 yes }.include?(value.to_s.strip.downcase) else self[name.to_sym] = nil end end end
define_date_accessor(name)
click to toggle source
# File lib/podio/active_podio/base.rb, line 378 def define_date_accessor(name) self.send(:define_method, name) do self[name.to_sym].try(:to_date) rescue nil end self.send(:define_method, "#{name}=") do |value| self[name.to_sym] = if value.is_a?(Date) value.try(:to_s, :db) else value = value.try(:to_s) if defined?(I18n) && value.present? && !(value =~ /^\d{4}-\d{2}-\d{2}$/) # If we have I18n available, assume that we are in Rails and try to convert the string to a date to convert it to ISO 8601 value_as_date = Date.strptime(value, I18n.t('date.formats.default')) rescue nil value_as_date.nil? ? value : value_as_date.try(:to_s, :db) else value.try(:presence) end end end end
define_datetime_accessor(name, options = {})
click to toggle source
# File lib/podio/active_podio/base.rb, line 349 def define_datetime_accessor(name, options = {}) self.send(:define_method, name) do options[:convert_timezone] == false ? self[name.to_sym].try(:to_datetime) : self[name.to_sym].try(:to_datetime).try(:in_time_zone) end self.send(:define_method, "#{name}=") do |value| # TODO: This should eventually be done on all date times # This option is a temporary fix while API transitions to UTC only if options[:convert_incoming_local_datetime_to_utc] && value.present? && !@values_from_api value = if value.is_a?(DateTime) Time.zone.local_to_utc(value) else begin Time.zone.parse(value).try(:utc).try(:to_datetime) rescue TZInfo::AmbiguousTime nil end end end self[name.to_sym] = if value.is_a?(DateTime) value.try(:to_s, :db) else value.try(:to_s).try(:presence) end end end
define_decimal_accessor(name)
click to toggle source
# File lib/podio/active_podio/base.rb, line 449 def define_decimal_accessor(name) self.send(:define_method, name) do if self[name.to_sym].present? BigDecimal(self[name.to_sym], 2) else nil end end self.send(:define_method, "#{name}=") do |value| if value.present? self[name.to_sym] = BigDecimal(value, 2) else self[name.to_sym] = nil end end end
define_float_accessor(name)
click to toggle source
# File lib/podio/active_podio/base.rb, line 431 def define_float_accessor(name) self.send(:define_method, name) do if self[name.to_sym].present? self[name.to_sym].to_f else nil end end self.send(:define_method, "#{name}=") do |value| if value.present? self[name.to_sym] = value.to_f else self[name.to_sym] = nil end end end
define_generic_accessor(name, options = {})
click to toggle source
# File lib/podio/active_podio/base.rb, line 333 def define_generic_accessor(name, options = {}) options.reverse_merge!(:getter => true, :setter => true) if(options[:getter]) self.send(:define_method, name) do self[name.to_sym] end end if(options[:setter]) self.send(:define_method, "#{name}=") do |value| self[name.to_sym] = value end end end
define_integer_accessor(name)
click to toggle source
# File lib/podio/active_podio/base.rb, line 413 def define_integer_accessor(name) self.send(:define_method, name) do if self[name.to_sym].present? self[name.to_sym].to_i else nil end end self.send(:define_method, "#{name}=") do |value| if value.present? self[name.to_sym] = value.to_i else self[name.to_sym] = nil end end end
define_time_accessor(name)
click to toggle source
# File lib/podio/active_podio/base.rb, line 398 def define_time_accessor(name) self.send(:define_method, name) do self[name.to_sym] end self.send(:define_method, "#{name}=") do |value| time = if value.is_a?(DateTime) || value.is_a?(Time) value else Time.strptime(value, I18n.t('time.formats.timeonly')) rescue Time.strptime(value, '%H:%M:%S') rescue value end self[name.to_sym] = time.respond_to?(:strftime) ? time.strftime('%H:%M') : time.presence end end
Public Instance Methods
==(other)
click to toggle source
# File lib/podio/active_podio/base.rb, line 89 def ==(other) !self.nil? && !other.nil? && self.respond_to?(:id) && other.respond_to?(:id) && self.id == other.id end
Also aliased as: eql?
[](attribute)
click to toggle source
# File lib/podio/active_podio/base.rb, line 75 def [](attribute) @attributes ||= {} @attributes[attribute.to_sym] end
[]=(attribute, value)
click to toggle source
# File lib/podio/active_podio/base.rb, line 80 def []=(attribute, value) @attributes ||= {} @attributes[attribute.to_sym] = value if @belongs_to.present? && @belongs_to[:model].present? && @belongs_to[:as].present? && value.present? @belongs_to[:model][@belongs_to[:as]] ||= {} @belongs_to[:model][@belongs_to[:as]][attribute.to_sym] = value end end
api_friendly_ref_type()
click to toggle source
Override this in models where the class name doesn’t match the ref type
# File lib/podio/active_podio/base.rb, line 143 def api_friendly_ref_type self.class.name.demodulize.parameterize end
as_json(options={})
click to toggle source
# File lib/podio/active_podio/base.rb, line 98 def as_json(options={}) options ||= {} result = {} result.merge!(:id => self.id) if self.respond_to?(:id) if options[:formatted] (self.valid_attributes + self.json_attributes).uniq.each do |name| result[name] = json_friendly_value(self.send(name), options) end unless options[:nested] == false self._associations.each do |name, type| nested_value = self.send(name) unless nested_value.nil? nested_options = options.except(:methods) if options[:methods].present? && options[:methods].respond_to?(:find) methods_hash = options[:methods].find { |method| method.is_a?(Hash) } if methods_hash.present? nested_methods = methods_hash[name] nested_options.merge!(:methods => nested_methods) if nested_methods.present? end end case type when :has_one result[name] = nested_value.as_json(nested_options) when :has_many result[name] = nested_value.collect { |assoc| assoc.as_json(nested_options) } end end end end else result.merge!(self.attributes) end if options[:methods] options[:methods].each do |name| result[name] = json_friendly_value(self.send(name), options.except(:methods) ) unless name.is_a?(Hash) end end result end
hash()
click to toggle source
# File lib/podio/active_podio/base.rb, line 94 def hash self.id.hash if self.respond_to?(:id) end
initialize_attributes(attributes)
click to toggle source
# File lib/podio/active_podio/base.rb, line 29 def initialize_attributes(attributes) attributes.each do |key, value| if self.respond_to?("#{key}=".to_sym) self.send("#{key}=".to_sym, value) else is_association_hash = value.is_a?(Hash) && self._associations.has_key?(key.to_sym) && self._associations[key.to_sym] == :has_one && (self.send(key.to_sym).respond_to?(:attributes) || self.send(key.to_sym).nil?) if valid_attributes.include?(key.to_sym) || is_association_hash # Initialize nested object to get correctly casted values set back, unless the given values are all blank if is_association_hash # Merge existing values with given values and do recursive initalize call to get values casted correctly if self.send(key.to_sym).present? existing_attributes = self.send(key.to_sym).try(:attributes) || {} value.reverse_merge!(existing_attributes) self.send(key.to_sym).initialize_attributes(value) else self.send(:[]=, key.to_sym, value) end attributes = self.send(key.to_sym).try(:attributes) if attributes.present? && any_values_present_recursive?(attributes.values) value = attributes else value = nil end end self.send(:[]=, key.to_sym, value) end end end end
new_record?()
click to toggle source
# File lib/podio/active_podio/base.rb, line 65 def new_record? ! (self.respond_to?(:id) && self.id.present?) end
parent_model()
click to toggle source
# File lib/podio/active_podio/base.rb, line 147 def parent_model @belongs_to[:model] if @belongs_to.present? end
persisted?()
click to toggle source
# File lib/podio/active_podio/base.rb, line 61 def persisted? ! self.new_record? end
to_param()
click to toggle source
# File lib/podio/active_podio/base.rb, line 69 def to_param local_id = self.id if self.respond_to?(:id) local_id = nil if local_id == self.object_id # Id still returns object_id in Ruby 1.8.7, JRuby and Rubinius local_id.try(:to_s) end
Private Instance Methods
any_values_present_recursive?(values)
click to toggle source
# File lib/podio/active_podio/base.rb, line 166 def any_values_present_recursive?(values) values.any? do |value| if value.respond_to?(:values) any_values_present_recursive?(value.values) else value.present? end end end
json_friendly_value(ruby_value, options)
click to toggle source
# File lib/podio/active_podio/base.rb, line 176 def json_friendly_value(ruby_value, options) if options[:formatted] case ruby_value.class.name when "DateTime", "Time" ruby_value.iso8601 when "Array" ruby_value.collect { |rv| json_friendly_value(rv, options) } when "Hash" ruby_value.each { |key, value| ruby_value[key] = json_friendly_value(value, options) } ruby_value when "BigDecimal" ruby_value.to_f # No Decimal in Javascript, Float is better than String else if ruby_value.kind_of?(ActivePodio::Base) ruby_value.as_json(options.except(:methods)) else ruby_value end end else ruby_value end end
klass_for_association(options)
click to toggle source
# File lib/podio/active_podio/base.rb, line 153 def klass_for_association(options) klass_name = options[:class] if !klass_name.present? && options[:class_map].present? class_property = options[:class_property] || :type class_property_value = self.send(class_property).try(:to_sym) klass_name = options[:class_map][class_property_value] end raise "Missing class name of associated model. Provide with :class => 'MyClass'." unless klass_name.present? return self.class.klass_from_string(klass_name) end