lib/her/model/relation.rb
module Her
module Model
class Relation
# @private
attr_accessor :params
attr_writer :parent
# @private
def initialize(parent)
@parent = parent
@params = {}
end
# @private
def apply_to(attributes)
@params.merge(attributes)
end
# Build a new resource
def build(attributes = {})
@parent.build(@params.merge(attributes))
end
# Add a query string parameter
#
# @example
# @users = User.all
# # Fetched via GET "/users"
#
# @example
# @users = User.where(:approved => 1).all
# # Fetched via GET "/users?approved=1"
def where(params = {})
return self if params.blank? && !@_fetch.nil?
clone.tap do |r|
r.params = r.params.merge(params)
r.clear_fetch_cache!
end
end
alias all where
# Bubble all methods to the fetched collection
#
# @private
def method_missing(method, *args, &blk)
fetch.send(method, *args, &blk)
end
# @private
def respond_to?(method, *args)
super || fetch.respond_to?(method, *args)
end
# @private
def nil?
fetch.nil?
end
# @private
def kind_of?(thing)
fetch.is_a?(thing)
end
# Fetch a collection of resources
#
# @private
def fetch
@_fetch ||= begin
path = @parent.build_request_path(@parent.collection_path, @params)
method = @parent.method_for(:find)
@parent.request(@params.merge(:_method => method, :_path => path)) do |parsed_data, _|
@parent.new_collection(parsed_data)
end
end
end
# Fetch specific resource(s) by their ID
#
# @example
# @user = User.find(1)
# # Fetched via GET "/users/1"
#
# @example
# @users = User.find([1, 2])
# # Fetched via GET "/users/1" and GET "/users/2"
def find(*ids)
params = @params.merge(ids.last.is_a?(Hash) ? ids.pop : {})
ids = Array(params[@parent.primary_key]) if params.key?(@parent.primary_key)
results = ids.flatten.compact.uniq.map do |id|
resource = nil
request_params = params.merge(
:_method => @parent.method_for(:find),
:_path => @parent.build_request_path(params.merge(@parent.primary_key => id))
)
@parent.request(request_params) do |parsed_data, response|
if response.success?
resource = @parent.new_from_parsed_data(parsed_data)
resource.run_callbacks :find
else
return nil
end
end
resource
end
ids.length > 1 || ids.first.is_a?(Array) ? results : results.first
end
# Fetch first resource with the given attributes.
#
# If no resource is found, returns <tt>nil</tt>.
#
# @example
# @user = User.find_by(name: "Tobias", age: 42)
# # Called via GET "/users?name=Tobias&age=42"
def find_by(params)
where(params).first
end
# Fetch first resource with the given attributes, or create a resource
# with the attributes if one is not found.
#
# @example
# @user = User.find_or_create_by(email: "remi@example.com")
#
# # Returns the first item in the collection if present:
# # Called via GET "/users?email=remi@example.com"
#
# # If collection is empty:
# # POST /users with `email=remi@example.com`
# @user.email # => "remi@example.com"
# @user.new? # => false
def find_or_create_by(attributes)
find_by(attributes) || create(attributes)
end
# Fetch first resource with the given attributes, or initialize a resource
# with the attributes if one is not found.
#
# @example
# @user = User.find_or_initialize_by(email: "remi@example.com")
#
# # Returns the first item in the collection if present:
# # Called via GET "/users?email=remi@example.com"
#
# # If collection is empty:
# @user.email # => "remi@example.com"
# @user.new? # => true
def find_or_initialize_by(attributes)
find_by(attributes) || build(attributes)
end
# Create a resource and return it
#
# @example
# @user = User.create(:fullname => "Tobias Fünke")
# # Called via POST "/users/1" with `&fullname=Tobias+Fünke`
#
# @example
# @user = User.where(:email => "tobias@bluth.com").create(:fullname => "Tobias Fünke")
# # Called via POST "/users/1" with `&email=tobias@bluth.com&fullname=Tobias+Fünke`
def create(attributes = {})
attributes ||= {}
resource = @parent.new(@params.merge(attributes))
resource.save
resource
end
# Fetch a resource and create it if it's not found
#
# @example
# @user = User.where(:email => "remi@example.com").find_or_create
#
# # Returns the first item of the collection if present:
# # GET "/users?email=remi@example.com"
#
# # If collection is empty:
# # POST /users with `email=remi@example.com`
def first_or_create(attributes = {})
fetch.first || create(attributes)
end
# Fetch a resource and build it if it's not found
#
# @example
# @user = User.where(:email => "remi@example.com").find_or_initialize
#
# # Returns the first item of the collection if present:
# # GET "/users?email=remi@example.com"
#
# # If collection is empty:
# @user.email # => "remi@example.com"
# @user.new? # => true
def first_or_initialize(attributes = {})
fetch.first || build(attributes)
end
# @private
def clear_fetch_cache!
instance_variable_set(:@_fetch, nil)
end
end
end
end