module SuperModel class Base class_inheritable_array :known_attributes self.known_attributes = [] class << self attr_accessor_with_default(:primary_key, 'id') #:nodoc: def collection(&block) @collection ||= Class.new(Array) @collection.class_eval(&block) if block_given? @collection end def attributes(*attributes) self.known_attributes += attributes.map(&:to_s) end def records @records ||= {} end def find_by_attribute(name, value) #:nodoc: item = records.values.find {|r| r.send(name) == value } item && item.dup end def find_all_by_attribute(name, value) #:nodoc: items = records.values.select {|r| r.send(name) == value } collection.new(items.deep_dup) end def raw_find(id) #:nodoc: records[id] || raise(UnknownRecord, "Couldn't find #{self.name} with ID=#{id}") end # Find record by ID, or raise. def find(id) item = raw_find(id) item && item.dup end alias :[] :find def first item = records.values[0] item && item.dup end def last item = records.values[-1] item && item.dup end def exists?(id) records.has_key?(id) end def count records.length end def all collection.new(records.values.deep_dup) end def select(&block) collection.new(records.values.select(&block).deep_dup) end def update(id, atts) find(id).update_attributes(atts) end def destroy(id) find(id).destroy end # Removes all records and executes # destory callbacks. def destroy_all all.each {|r| r.destroy } end # Removes all records without executing # destroy callbacks. def delete_all records.clear end # Create a new record. # Example: # create(:name => "foo", :id => 1) def create(atts = {}) rec = self.new(atts) rec.save && rec end def create!(*args) create(*args) || raise(InvalidRecord) end def method_missing(method_symbol, *args) #:nodoc: method_name = method_symbol.to_s if method_name =~ /^find_by_(\w+)!/ send("find_by_#{$1}", *args) || raise(UnknownRecord) elsif method_name =~ /^find_by_(\w+)/ find_by_attribute($1, args.first) elsif method_name =~ /^find_or_create_by_(\w+)/ send("find_by_#{$1}", *args) || create($1 => args.first) elsif method_name =~ /^find_all_by_(\w+)/ find_all_by_attribute($1, args.first) else super end end end attr_accessor :attributes attr_writer :new_record def known_attributes self.class.known_attributes + self.attributes.keys.map(&:to_s) end def initialize(attributes = {}) @new_record = true @attributes = {}.with_indifferent_access @changed_attributes = {} load(attributes) end def clone cloned = attributes.reject {|k,v| k == self.class.primary_key } cloned = cloned.inject({}) do |attrs, (k, v)| attrs[k] = v.clone attrs end self.class.new(cloned) end def new? @new_record || false end alias :new_record? :new? # Gets the \id attribute of the item. def id attributes[self.class.primary_key] end # Sets the \id attribute of the item. def id=(id) attributes[self.class.primary_key] = id end def ==(other) other.equal?(self) || (other.instance_of?(self.class) && other.id == id) end # Tests for equality (delegates to ==). def eql?(other) self == other end def hash id.hash end def dup self.class.new.tap do |base| base.attributes = attributes base.new_record = new_record? end end def save new? ? create : update end def save! save || raise(InvalidRecord) end def exists? !new? end def load(attributes) #:nodoc: attributes.each do |(name, value)| self.send("#{name}=".to_sym, value) end end def reload return self if new? item = self.class.find(id) load(item.attributes) return self end def update_attribute(name, value) self.send("#{name}=".to_sym, value) self.save end def update_attributes(attributes) load(attributes) && save end def update_attributes!(attributes) update_attributes(attributes) || raise(InvalidRecord) end def has_attribute?(name) @attributes.has_key?(name) end alias_method :respond_to_without_attributes?, :respond_to? def respond_to?(method, include_priv = false) method_name = method.to_s if attributes.nil? super elsif known_attributes.include?(method_name) true elsif method_name =~ /(?:=|\?)$/ && attributes.include?($`) true else super end end def destroy raw_destroy self end protected def read_attribute(name) @attributes[name] end def write_attribute(name, value) @attributes[name] = value end def generate_id object_id end def raw_destroy self.class.records.delete(self.id) end def raw_create self.class.records[self.id] = self.dup end def create self.id ||= generate_id self.new_record = false raw_create self.id end def raw_update item = self.class.raw_find(id) item.load(attributes) end def update raw_update true end private def method_missing(method_symbol, *arguments) #:nodoc: method_name = method_symbol.to_s if method_name =~ /(=|\?)$/ case $1 when "=" attribute_will_change!($`) attributes[$`] = arguments.first when "?" attributes[$`] end else return attributes[method_name] if attributes.include?(method_name) return nil if known_attributes.include?(method_name) super end end end class Base extend ActiveModel::Naming include ActiveModel::Conversion include ActiveModel::Serializers::JSON include ActiveModel::Serializers::Xml include Dirty, Observing, Callbacks, Validations include Association::Model end end