module SugarCRM; module AttributeMethods
module ClassMethods
# Returns a hash of the module fields from the module
def attributes_from_module
fields = {}.with_indifferent_access
self._module.fields.keys.sort.each do |k|
fields[k] = nil
end
fields
end
# Returns the table name for a given attribute
def table_name_for(attribute)
table_name = self._module.table_name
if attribute.to_s =~ /_c$/
table_name = self._module.custom_table_name
end
table_name
end
# Takes a condition like: [:zip, ["> 75000", "< 80000"]]
# and flattens it to: ["accounts.zip > 75000", "accounts.zip < 80000"]
def flatten_conditions_for(condition)
conditions = []
attribute, attribute_conditions = condition
# Make sure we wrap the attribute condition in an array for EZ handling...
Array.wrap(attribute_conditions).each do |attribute_condition|
# parse operator in cases where:
# :attribute => '>= some_value',
# :attribute => "LIKE '%value%'",
# fallback to '=' operator as default]
operator = attribute_condition.to_s[/^([!<>=]*(LIKE|IS|NOT|\s)*)(.*)$/,1].strip!
# Default to = if we can't resolve the condition.
operator ||= '='
# Extract value from query
value = $3
# TODO: Write a test for sending invalid attribute names.
# strip single quotes
value = value.strip[/'?([^']*)'?/,1]
conditions << "#{table_name_for(attribute)}.#{attribute} #{operator} \'#{value}\'"
end
conditions
end
end
# Determines if attributes or associations have been changed
def changed?
return true if attributes_changed?
return true if associations_changed?
false
end
def attributes_changed?
@modified_attributes.length > 0
end
# Is this a new record?
def new?
@attributes[:id].blank?
end
# List the required attributes for save
def required_attributes
self.class._module.required_fields
end
protected
# Merges attributes provided as an argument to initialize
# with attributes from the module.fields array. Skips any
# fields that aren't in the module.fields array
#
# BUG: SugarCRM likes to return fields you don't ask for and
# aren't fields on a module (i.e. modified_user_name). This
# royally screws up our typecasting code, so we handle it here.
def merge_attributes(attrs={})
# copy attributes from the parent module fields array
@attributes = self.class.attributes_from_module
# populate the attributes with values from the attrs provided to init.
@attributes.keys.each do |name|
write_attribute name, attrs[name] if attrs[name]
end
# If this is an existing record, blank out the modified_attributes hash
@modified_attributes = {} unless new?
end
# Generates get/set methods for keys in the attributes hash
def define_attribute_methods
return if attribute_methods_generated?
@attributes.keys.sort.each do |k|
self.class.module_eval %Q?
def #{k}
read_attribute :#{k}
end
def #{k}=(value)
write_attribute :#{k},value
end
?
end
self.class.attribute_methods_generated = true
end
# Returns an #inspect-like string for the value of the
# attribute +attr_name+. String attributes are elided after 50
# characters, and Date and Time attributes are returned in the
# :db format. Other attributes return the value of
# #inspect without modification.
#
# person = Person.create!(:name => "David Heinemeier Hansson " * 3)
#
# person.attribute_for_inspect(:name)
# # => '"David Heinemeier Hansson David Heinemeier Hansson D..."'
#
# person.attribute_for_inspect(:created_at)
# # => '"2009-01-12 04:48:57"'
def attribute_for_inspect(attr_name)
value = read_attribute(attr_name)
if value.is_a?(String) && value.length > 50
"#{value[0..50]}...".inspect
elsif value.is_a?(Date) || value.is_a?(Time)
%("#{value.to_s(:db)}")
else
value.inspect
end
end
# Wrapper for invoking save on modified_attributes
# sets the id if it's a new record
def save_modified_attributes!
# Complain if we aren't valid
raise InvalidRecord, errors.to_a.join(", ") if !valid?
# Send the save request
response = SugarCRM.connection.set_entry(self.class._module.name, serialize_modified_attributes)
# Complain if we don't get a parseable response back
raise RecordsaveFailed, "Failed to save record: #{self}. Response was not a Hash" unless response.is_a? Hash
# Complain if we don't get a valid id back
raise RecordSaveFailed, "Failed to save record: #{self}. Response did not contain a valid 'id'." if response["id"].nil?
# Save the id to the record, if it's a new record
@attributes[:id] = response["id"] if new?
raise InvalidRecord, "Failed to update id for: #{self}." if id.nil?
# Clear the modified attributes Hash
@modified_attributes = {}
true
end
# Wrapper around attributes hash
def read_attribute(key)
@attributes[key]
end
# Wrapper around attributes hash
def write_attribute(key, value)
@modified_attributes[key] = { :old => @attributes[key].to_s, :new => value }
@attributes[key] = value
end
end; end