module ActiveResource::Associations module Builder autoload :Association, 'active_resource/associations/builder/association' autoload :HasMany, 'active_resource/associations/builder/has_many' autoload :HasOne, 'active_resource/associations/builder/has_one' # autoload :BelongsTo, 'active_resource/associations/builder/belongs_to' require_relative 'associations/builder/belongs_to' end # Specifies a one-to-many association. # # === Options # [:class_name] # Specify the class name of the association. This class name would # be used for resolving the association class. # # ==== Example for [:class_name] - option # GET /posts/123.json delivers following response body: # { # title: "ActiveResource now has associations", # body: "Lorem Ipsum" # comments: [ # { # content: "..." # }, # { # content: "..." # } # ] # } # ==== # # has_many :comments, :class_name => 'myblog/comment' # Would resolve those comments into the Myblog::Comment class. # # If the response body does not contain an attribute matching the association name # a request sent to the index action under the current resource. # For the example above, if the comments are not present the requested path would be: # GET /posts/123/comments.xml def has_many(name, options = {}) Builder::HasMany.build(self, name, options) end # Specifies a one-to-one association. # # === Options # [:class_name] # Specify the class name of the association. This class name would # be used for resolving the association class. # # ==== Example for [:class_name] - option # GET /posts/1.json delivers following response body: # { # title: "ActiveResource now has associations", # body: "Lorem Ipsum", # author: { # name: "Gabby Blogger", # } # } # ==== # # has_one :author, :class_name => 'myblog/author' # Would resolve this author into the Myblog::Author class. # # If the response body does not contain an attribute matching the association name # a request is sent to a singelton path under the current resource. # For example, if a Product class has_one :inventory calling Product#inventory # will generate a request on /product/:product_id/inventory.json. # def has_one(name, options = {}) Builder::HasOne.build(self, name, options) end # Specifies a one-to-one association with another class. This class should only be used # if this class contains the foreign key. # # Methods will be added for retrieval and query for a single associated object, for which # this object holds an id: # # [association(force_reload = false)] # Returns the associated object. +nil+ is returned if the foreign key is +nil+. # Throws a ActiveResource::ResourceNotFound exception if the foreign key is not +nil+ # and the resource is not found. # # (+association+ is replaced with the symbol passed as the first argument, so # belongs_to :post would add among others post.nil?. # # === Example # # A Comment class declaress belongs_to :post, which will add: # * Comment#post (similar to Post.find(post_id)) # The declaration can also include an options hash to specialize the behavior of the association. # # === Options # [:class_name] # Specify the class name for the association. Use it only if that name canÄt be inferred from association name. # So belongs_to :post will by default be linked to the Post class, but if the real class name is Article, # you'll have to specify it with whis option. # [:foreign_key] # Specify the foreign key used for the association. By default this is guessed to be the name # of the association with an "_id" suffix. So a class that defines a belongs_to :post # association will use "post_id" as the default :foreign_key. Similarly, # belongs_to :article, :class_name => "Post" will use a foreign key # of "article_id". # # Option examples: # belongs_to :customer, :class_name => 'User' # Creates a belongs_to association called customer which is represented through the User class. # # belongs_to :customer, :foreign_key => 'user_id' # Creates a belongs_to association called customer which would be resolved by the foreign_key user_id instead of customer_id # def belongs_to(name, options={}) Builder::BelongsTo.build(self, name, options) end # Defines the belongs_to association finder method def defines_belongs_to_finder_method(method_name, association_model, finder_key) ivar_name = :"@#{method_name}" if method_defined?(method_name) instance_variable_set(ivar_name, nil) remove_method(method_name) end define_method(method_name) do if instance_variable_defined?(ivar_name) instance_variable_get(ivar_name) elsif attributes.include?(method_name) attributes[method_name] elsif association_id = send(finder_key) return nil if association_id.blank? instance_variable_set(ivar_name, association_model.find(association_id)) end end define_method("#{method_name}=") do |obj| instance_variable_set(ivar_name, obj) attributes["#{method_name}_id"] = obj.try(:id) end end attr_accessor :embeds def defines_belongs_to_embed(method_name, association_model, foreign_key) self.embeds ||= [] self.embeds << association_model.to_s.underscore end def defines_has_many_finder_method(method_name, association_model) ivar_name = :"@#{method_name}" define_method(method_name) do if instance_variable_defined?(ivar_name) instance_variable_get(ivar_name) elsif attributes.include?(method_name) attributes[method_name] else instance_variable_set(ivar_name, association_model.find(:all, :params => { :q => {:"#{self.class.element_name}_id" => self.id} })) end end end # Defines the has_one association def defines_has_one_finder_method(method_name, association_model) ivar_name = :"@#{method_name}" define_method(method_name) do if instance_variable_defined?(ivar_name) instance_variable_get(ivar_name) elsif attributes.include?(method_name) attributes[method_name] else instance_variable_set(ivar_name, association_model.find(:params => { :q => { :"#{self.class.element_name}_id" => self.id} })) end end end end