# frozen_string_literal: true
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
attr_accessor :embeds
def defines_belongs_to_embed(reflection)
self.embeds ||= []
self.embeds << reflection.name.to_s
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 singleton path under the current resource.
# For example, if a Product class has_one :inventory calling Product#inventory
# will generate a request on /products/: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 declares 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 this 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(reflection)
method_name = reflection.name
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(reflection.foreign_key)
return nil if association_id.blank?
instance_variable_set(ivar_name, (reflection.klass.find(association_id) rescue nil))
end
end
define_method("#{method_name}=") do |obj|
instance_variable_set(ivar_name, obj)
attributes["#{method_name}_id"] = obj.try(:id)
end
end
def defines_has_many_finder_method(reflection)
method_name = reflection.name
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]
elsif !new_record?
instance_variable_set(ivar_name, reflection.klass.find(:all, params: { q: { :"#{self.class.element_name}_id" => self.id } }))
else
instance_variable_set(ivar_name, self.class.collection_parser.new)
end
end
end
# Defines the has_one association
def defines_has_one_finder_method(reflection)
method_name = reflection.name
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]
elsif reflection.klass.respond_to?(:singleton_name)
instance_variable_set(ivar_name, reflection.klass.find(params: { q: { :"#{self.class.element_name}_id" => self.id } }))
else
instance_variable_set(ivar_name, reflection.klass.find(:one, from: "/#{self.class.collection_name}/#{self.id}/#{method_name}#{self.class.format_extension}"))
end
end
end
end