0
- SPECIAL_MEMBERS = %w(attributes associations connection callbacks)
0
- DEFAULT_ATTRIBUTES = %w(id rev)
0
- def initialize(params = {})
0
- # Object instance variable
0
- @attributes = {}; @associations = {}; @callbacks = Hash.new; @connection = self.class.connection
0
- klass_atts = self.class.attributes; klass_assocs = self.class.associations; klass_callbacks = self.class.callbacks
0
- # ActiveCouch::Connection object will be readable in every
0
- # object instantiated from a subclass of ActiveCouch::Base
0
- SPECIAL_MEMBERS.each do |k|
0
- self.instance_eval "def #{k}; @#{k}; end"
0
- klass_atts.each_key do |k|
0
- @attributes[k] = klass_atts[k].dup
0
- self.instance_eval "def #{k}; attributes[:#{k}].value; end"
0
- self.instance_eval "def #{k}=(val); attributes[:#{k}].value = val; end"
0
- klass_assocs.each_key do |k|
0
- @associations[k] = HasManyAssociation.new(klass_assocs[k].name, :class => klass_assocs[k].klass)
0
- self.instance_eval "def #{k}; associations[:#{k}].container; end"
0
- # If you have has_many :people, this will add a method called add_person to the object instantiated
0
- self.instance_eval "def add_#{Inflector.singularize(k)}(val); associations[:#{k}].push(val); end"
0
- klass_callbacks.each_key do |k|
0
- @callbacks[k] = klass_callbacks[k].dup
0
- DEFAULT_ATTRIBUTES.each do |x|
0
- self.instance_eval "def #{x}; _#{x}; end"
0
- self.instance_eval "def #{x}=(val); self._#{x}=(val); end"
0
- # Set any instance variables if any, which are present in the params hash
0
- # Generates a JSON representation of an instance of a subclass of ActiveCouch::Base.
0
- # Ignores attributes which have a nil value.
0
- # class Person < ActiveCouch::Base
0
- # has :name, :which_is => :text, :with_default_value => "McLovin"
0
- # person.to_json # {"name":"McLovin"}
0
- # class AgedPerson < ActiveCouch::Base
0
- # has :age, :which_is => :decimal, :with_default_value => 3.5
0
- # aged_person = AgedPerson.new
0
- # aged_person.id = 'abc-def'
0
- # aged_person.to_json # {"age":3.5, "_id":"abc-def"}
0
- attributes.each_value { |v| hash.merge!(v.to_hash) unless v.nil? }
0
- associations.each_value { |v| hash.merge!(v.to_hash) }
0
- # and by the Power of Grayskull, convert the hash to json
0
- # Saves a document into a CouchDB database. A document can be saved in two ways.
0
- # One if it has been set an ID by the user, in which case the connection object
0
- # needs to use an HTTP PUT request to the URL /database/user_generated_id.
0
- # For the document needs a CouchDB-generated ID, the connection object needs
0
- # to use an HTTP POST request to the URL /database.
0
- # class Person < ActiveCouch::Base
0
- # has :name, :which_is => :text
0
- # person = Person.new(:name => 'McLovin')
0
- response = connection.put("/#{self.class.database_name}/#{id}", to_json)
0
- response = connection.post("/#{self.class.database_name}", to_json)
0
- # Parse the JSON obtained from the body...
0
- results = JSON.parse(response.body)
0
- # ...and set the default id and rev attributes
0
- DEFAULT_ATTRIBUTES.each { |a| self.__send__("#{a}=", results[a]) }
0
- # Response sent will be 201, if the save was successful [201 corresponds to 'created']
0
- return response.code == '201'
0
- # Checks to see if a document has been persisted in a CouchDB database.
0
- # If a document has been retrieved from CouchDB, or has been persisted in
0
- # a CouchDB database, the attribute _rev would not be nil.
0
- # class Person < ActiveCouch::Base
0
- # has :name, :which_is => :text
0
- # person = Person.new(:name => 'McLovin')
0
- # Deletes a document from a CouchDB database. This is an instance-level delete method.
0
- # class Person < ActiveCouch::Base
0
- # person = Person.create(:name => 'McLovin')
0
- # person.delete # true
0
- raise ArgumentError, "You must specify a revision for the document to be deleted"
0
- raise ArgumentError, "You must specify an ID for the document to be deleted"
0
- response = connection.delete("/#{self.class.database_name}/#{id}?rev=#{rev}")
0
- # Set the id and rev to nil, since the object has been successfully deleted from CouchDB
0
- if response.code == '202'
0
- self.id = nil; self.rev = nil
0
- class << self # Class methods
0
- # Returns the CouchDB database name that's backing this model. The database name is guessed from the name of the
0
- # class somewhat similar to ActiveRecord conventions.
0
- # class Invoice < ActiveCouch::Base; end;
0
- # file class database_name
0
- # invoice.rb Invoice invoices
0
- # class Invoice < ActiveCouch::Base; class Lineitem < ActiveCouch::Base; end; end;
0
- # file class database_name
0
- # invoice.rb Invoice::Lineitem invoice_lineitems
0
- # module Invoice; class Lineitem < ActiveCouch::Base; end; end;
0
- # file class database_name
0
- # invoice/lineitem.rb Invoice::Lineitem lineitems
0
- # You can override this method or use <tt>set_database_name</tt> to override this class method to allow for names
0
- # that can't be inferred.
0
- name = (unless self == base
0
- # Nested classes are prefixed with singular parent database name.
0
- if parent < ActiveCouch::Base
0
- contained = Inflector.singularize(parent.database_name)
0
- "#{contained}#{Inflector.underscore(Inflector.demodulize(Inflector.pluralize(base.name)))}"
0
- set_database_name(name)
0
- # Sets the database name to the given value, or (if the value is nil or false) to the value returned by the
0
- # given block. Useful for setting database names that can't be automatically inferred from the class name.
0
- # This method is aliased as <tt>database_name=</tt>.
0
- # class Post < ActiveCouch::Base
0
- # set_database_name 'legacy_posts'
0
- def set_database_name(database = nil, &block)
0
- define_attr_method(:database_name, database, &block)
0
- alias :database_name= :set_database_name
0
- # Sets the site which the ActiveCouch object has to connect to, which
0
- # initializes an ActiveCouch::Connection object.
0
- # class Person < ActiveCouch::Base
0
- # site 'localhost:5984'
0
- # Person.connection.nil? # false
0
- @connection = Connection.new(site)
0
- # Defines an attribute for a subclass of ActiveCouch::Base. The parameters
0
- # for this method include name, which is the name of the attribute as well as
0
- # The options hash can contain the key 'which_is' which can
0
- # have possible values :text, :decimal, :number. It can also contain the key
0
- # 'with_default_value' which can set a default value for each attribute defined
0
- # in the subclass of ActiveCouch::Base
0
- # class Person < ActiveCouch::Base
0
- # p.name.methods.include?(:name) # true
0
- # p.name.methods.include?(:name=) # false
0
- # class AgedPerson < ActiveCouch::Base
0
- # has :age, :which_is => :number, :with_default_value = 18
0
- # person = AgedPerson.new
0
- def has(name, options = {})
0
- unless name.is_a?(String) || name.is_a?(Symbol)
0
- raise ArgumentError, "#{name} is neither a String nor a Symbol"
0
- @attributes[name] = Attribute.new(name, options)
0
- # Defines an array of objects which are 'children' of this class. The has_many
0
- # function guesses the class of the child, based on the name of the association,
0
- # but can be over-ridden by the :class key in the options hash.
0
- # class Person < ActiveCouch::Base
0
- # class GrandPerson < ActiveCouch::Base
0
- # has_many :people # which will create an empty array which can contain
0
- def has_many(name, options = {})
0
- unless name.is_a?(String) || name.is_a?(Symbol)
0
- raise ArgumentError, "#{name} is neither a String nor a Symbol"
0
- @associations[name] = HasManyAssociation.new(name, options)
0
- # Initializes an object of a subclass of ActiveCouch::Base based on a JSON
0
- # representation of the object.
0
- # class Person < ActiveCouch::Base
0
- # person = Person.from_json('{"name":"McLovin"}')
0
- # person.name # "McLovin"
0
- hash = JSON.parse(json)
0
- # Create new based on parsed
0
- # Retrieves one or more object(s) from a CouchDB database, based on the search
0
- # class Person < ActiveCouch::Base
0
- # # This returns a single instance of an ActiveCouch::Base subclass
0
- # people = Person.find(:first, :params => {:name => "McLovin"})
0
- # # This returns an array of ActiveCouch::Base subclass instances
0
- # person = Person.find(:all, :params => {:name => "McLovin"})
0
- scope = arguments.slice!(0)
0
- options = arguments.slice!(0) || {}
0
- when :all then find_every(options)
0
- when :first then find_every(options).first
0
- else raise ArgumentError("find must have the first parameter as either :all or :first")
0
- # Initializes a new subclass of ActiveCouch::Base and saves in the CouchDB database
0
- # class Person < ActiveCouch::Base
0
- # person = Person.create(:name => "McLovin")
0
- # person.id.nil? # false
0
- unless arguments.is_a?(Hash)
0
- raise ArgumentError, "The arguments must be a Hash"
0
- new_record = self.new(arguments)
0
- # Deletes a document from the CouchDB database, based on the id and rev parameters passed to it.
0
- # Returns true if the document has been deleted
0
- # class Person < ActiveCouch::Base
0
- # Person.delete(:id => 'abc-def', :rev => '1235')
0
- def delete(options = {})
0
- if options.nil? || !options.has_key?(:id) || !options.has_key?(:rev)
0
- raise ArgumentError, "You must specify both an id and a rev for the document to be deleted"
0
- response = connection.delete("/#{self.database_name}/#{options[:id]}?rev=#{options[:rev]}")
0
- response.code == '202'
0
- # Defines an "attribute" method. A new (class) method will be created with the
0
- # given name. If a value is specified, the new method will
0
- # return that value (as a string). Otherwise, the given block
0
- # will be used to compute the value of the method.
0
- # The original method, if it exists, will be aliased, with the
0
- # prefixed with "original_". This allows the new method to
0
- # access the original value.
0
- # This method is stolen from ActiveRecord.
0
- # class Foo < ActiveCouch::Base
0
- # define_attr_method :database_name, 'foo'
0
- # define_attr_method(:database_name) do
0
- # original_database_name + '_legacy'
0
- def define_attr_method(name, value = nil, &block)
0
- metaclass.send(:alias_method, "original_#{name}", name)
0
- metaclass.class_eval "def #{name}; #{value.to_s.inspect}; end"
0
- def inherited(subklass)
0
- subklass.class_eval do
0
- include ActiveCouch::Callbacks
0
- # TODO: Need a cleaner way to do this
0
- subklass.instance_variable_set "@attributes", { :_id => Attribute.new(:_id, :with_default_value => nil),
0
- :_rev => Attribute.new(:_rev, :with_default_value => nil) }
0
- subklass.instance_variable_set "@associations", {}
0
- subklass.instance_variable_set "@callbacks", Hash.new([])
0
- subklass.instance_variable_set "@connections", nil
0
- SPECIAL_MEMBERS.each do |k|
0
- subklass.instance_eval "def #{k}; @#{k}; end"
0
- class_of_active_couch_descendant(self)
0
- # Returns the class descending directly from ActiveCouch in the inheritance hierarchy.
0
- def class_of_active_couch_descendant(klass)
0
- if klass.superclass == Base
0
- elsif klass.superclass.nil?
0
- raise ActiveCouchError, "#{name} doesn't belong in a hierarchy descending from ActiveCouch"
0
- class_of_active_couch_descendant(klass.superclass)
0
- # Returns an array of ActiveCouch::Base objects by querying a CouchDB permanent view
0
- def find_every(options)
0
- case from = options[:from]
0
- path = "/#{database_name}/_view/#{query_string(options[:params])}"
0
- instantiate_collection(connection.get(path))
0
- # Generates a query string by using the ActiveCouch convention, which is to
0
- # have the view defined by pre-pending the attribute to be queried with 'by_'
0
- # So for example, if the params hash is :name => 'McLovin',
0
- # the view associated with it will be /by_name/by_name?key="McLovin"
0
- def query_string(params)
0
- params.each { |k,v| return "by_#{k}/by_#{k}?key=#{v.url_encode}" }
0
- raise ArgumentError, "The value for the key 'params' must be a Hash"
0
- # Instantiates a collection of ActiveCouch::Base objects, based on the
0
- # result obtained from a CouchDB View.
0
- # As per the CouchDB Permanent View API, the result set will be contained
0
- # within a JSON hash as an array, with the key 'rows'
0
- # The actual CouchDB object which needs to be initialized is obtained with
0
- def instantiate_collection(result)
0
- hash = JSON.parse(result)
0
- hash['rows'].collect { |row| self.new(row['value']) }
0
- end # End class methods
0
- # - Clean this up. Doesn't look very nice
0
- # - Raise errors if attribute/association is not present
0
- if v.is_a?(Array) # This means this is a has_many association
0
- unless (assoc = @associations[k]).nil?
0
- name, child_klass = assoc.name, assoc.klass
0
- child.is_a?(Hash) ? child_obj = child_klass.new(child) : child_obj = child
0
- self.send "add_#{Inflector.singularize(name)}", child_obj
0
- elsif v.is_a?(Hash) # This means this is a has_one association (which we might add later)
0
- # Do nothing for now. More later
0
- else # This means this is a normal attribute
0
- self.send("#{k}=", v) if respond_to?("#{k}=") # @attributes.has_key?(k) || @attributes.has_key?("_#{k}")
0
-end # End module ActiveCouch