Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add scoping. Remove metaprogramming. #302

Merged
merged 1 commit into from
Jan 6, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion lib/active_fedora.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class IllegalOperation < RuntimeError; end # :nodoc:


eager_autoload do
autoload :AssociationRelation
autoload :Associations
autoload :Attributes
autoload :Auditable
Expand All @@ -47,6 +48,7 @@ class IllegalOperation < RuntimeError; end # :nodoc:
autoload :NestedAttributes
autoload :NomDatastream
autoload :NtriplesRDFDatastream
autoload :NullRelation
autoload :OmDatastream
autoload :Property
autoload :Persistence
Expand All @@ -60,8 +62,17 @@ class IllegalOperation < RuntimeError; end # :nodoc:
autoload :RdfxmlRDFDatastream
autoload :Reflection
autoload :Relation

autoload_under 'relation' do
autoload :Delegation
autoload :SpawnMethods
autoload :QueryMethods
autoload :FinderMethods
end

autoload :RelationshipGraph
autoload :RelsExtDatastream
autoload :Scoping
autoload :SemanticNode
autoload :ServiceDefinitions
autoload :Serialization
Expand All @@ -77,7 +88,17 @@ class IllegalOperation < RuntimeError; end # :nodoc:
autoload :Validations
autoload :SolrInstanceLoader
end


module Scoping
extend ActiveSupport::Autoload

eager_autoload do
autoload :Default
autoload :Named
end
end



include Loggable

Expand Down
18 changes: 18 additions & 0 deletions lib/active_fedora/association_relation.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
module ActiveFedora
class AssociationRelation < Relation
def initialize(klass, association)
super(klass)
@association = association
end

def proxy_association
@association
end

private

def exec_queries
super.each { |r| @association.set_inverse_instance r }
end
end
end
1 change: 1 addition & 0 deletions lib/active_fedora/associations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ module Associations
extend ActiveSupport::Concern

autoload :Association, 'active_fedora/associations/association'
autoload :AssociationScope, 'active_fedora/associations/association_scope'
autoload :SingularAssociation, 'active_fedora/associations/singular_association'
autoload :CollectionAssociation, 'active_fedora/associations/collection_association'
autoload :CollectionProxy, 'active_fedora/associations/collection_proxy'
Expand Down
25 changes: 22 additions & 3 deletions lib/active_fedora/associations/association.rb
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,21 @@ def target=(target)
loaded!
end

# def scoped
# target_scope.merge(@association_scope)
# end
def scope
target_scope.merge(association_scope)
end

# The scope for this association.
#
# Note that the association_scope is merged into the target_scope only when the
# scope method is called. This is because at that point the call may be surrounded
# by scope.scoping { ... } or with_scope { ... } etc, which affects the scope which
# actually gets built.
def association_scope
if klass
@association_scope ||= AssociationScope.new(self).scope
end
end

# Set the inverse association, if possible
def set_inverse_instance(record)
Expand All @@ -73,6 +85,13 @@ def set_inverse_instance(record)
end
end


# Can be overridden (i.e. in ThroughAssociation) to merge in other scopes (i.e. the
# through association's scope)
def target_scope
klass.all
end

# Loads the \target if needed and returns it.
#
# This method is abstract in the sense that it relies on +find_target+,
Expand Down
33 changes: 33 additions & 0 deletions lib/active_fedora/associations/association_scope.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
module ActiveFedora
module Associations
class AssociationScope #:nodoc:

attr_reader :association

delegate :klass, :owner, :reflection, :interpolate, :to => :association
delegate :chain, :scope_chain, :options, :source_options, :active_record, :to => :reflection

def initialize(association)
@association = association
end


def scope
scope = klass.unscoped
add_constraints(scope)
end

private
def add_constraints(scope)
chain.each_with_index do |reflection, i|
is_first_chain = i == 0
klass = is_first_chain ? self.klass : reflection.klass
scope.where_values[options[:property]] = owner.id
end

scope
end

end
end
end
20 changes: 20 additions & 0 deletions lib/active_fedora/associations/collection_association.rb
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,17 @@ def replace(other_array)
concat(other_array.select { |v| !current.include?(v) })
end

def include?(record)
if record.is_a?(reflection.klass)
if record.new_record?
target.include?(record)
else
loaded? ? target.include?(record) : scope.exists?(record)
end
else
false
end
end

def to_ary
load_target.dup
Expand Down Expand Up @@ -273,6 +284,15 @@ def add_to_target(record)
record
end

def scope(opts = {})
scope = super()
scope.none! if opts.fetch(:nullify, true) && null_scope?
scope
end

def null_scope?
owner.new_record? && !foreign_key_present?
end
protected
def reset_target!
@target = Array.new
Expand Down
10 changes: 3 additions & 7 deletions lib/active_fedora/associations/collection_proxy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,7 @@ module Associations
#
# is computed directly through SQL and does not trigger by itself the
# instantiation of the actual post records.
class CollectionProxy # :nodoc:
alias :proxy_extend :extend

#TODO remove using: https://github.com/rails/rails/commit/c86a32d7451c5d901620ac58630460915292f88b#diff-bee622c17de67f292adf310f75288e25
instance_methods.each { |m| undef_method m unless m.to_s =~ /^(?:nil\?|send|object_id|to_a)$|^__|^respond_to|proxy_/ }

class CollectionProxy < Relation # :nodoc:
delegate :group, :order, :limit, :joins, :where, :preload, :eager_load, :includes, :from,
:lock, :readonly, :having, :to => :scoped

Expand All @@ -54,7 +49,8 @@ class CollectionProxy # :nodoc:

def initialize(association)
@association = association
Array(association.options[:extend]).each { |ext| proxy_extend(ext) }
super association.klass
merge! association.scope
end

def respond_to?(*args)
Expand Down
1 change: 1 addition & 0 deletions lib/active_fedora/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class Base
include SemanticNode
include Sharding
include ActiveFedora::Persistence
include Scoping
include Loggable
include Indexing
include ActiveModel::Conversion
Expand Down
8 changes: 8 additions & 0 deletions lib/active_fedora/core.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
module ActiveFedora
module Core
extend ActiveSupport::Concern

attr_reader :inner_object

# Constructor. You may supply a custom +:pid+, or we call the Fedora Rest API for the
Expand Down Expand Up @@ -139,5 +141,11 @@ def reify!
self.init_with DigitalObject.find(self.class,self.pid)
end

module ClassMethods
private
def relation
Relation.new(self)
end
end
end
end
7 changes: 7 additions & 0 deletions lib/active_fedora/null_relation.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module ActiveFedora
module NullRelation # :nodoc:
def exists?(_id = false)
false
end
end
end
17 changes: 1 addition & 16 deletions lib/active_fedora/querying.rb
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
module ActiveFedora
module Querying
delegate :find, :first, :where, :limit, :order, :all, :delete_all, :destroy_all, :count, :last, :to=>:relation
delegate :find, :first, :exists?, :where, :limit, :order, :delete_all, :destroy_all, :count, :last, :to=>:all

def self.extended(base)
base.class_attribute :solr_query_handler
base.solr_query_handler = 'standard'
end


def relation
Relation.new(self)
end

# Yields each batch of solr records that was found by the find +options+ as
# an array. The size of each batch is set by the <tt>:batch_size</tt>
Expand Down Expand Up @@ -73,16 +68,6 @@ def find_each( conditions={}, opts={})
end


# Returns true if the pid exists in the repository
# @param[String] pid
# @return[boolean]
def exists?(pid)
return false if pid.nil? || pid.empty?
!!DigitalObject.find(self, pid)
rescue ActiveFedora::ObjectNotFoundError
false
end

# Returns a solr result matching the supplied conditions
# @param[Hash,String] conditions can either be specified as a string, or
# hash representing the query part of an solr statement. If a hash is
Expand Down
8 changes: 5 additions & 3 deletions lib/active_fedora/reflection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -165,9 +165,11 @@ def foreign_key
@foreign_key ||= options[:foreign_key] || derive_foreign_key
end

# def primary_key_name
# @primary_key_name ||= options[:foreign_key] || derive_primary_key_name
# end
# A chain of reflections from this one back to the owner. For more see the explanation in
# ThroughReflection.
def chain
[self]
end

def inverse_of
return unless inverse_name
Expand Down
58 changes: 35 additions & 23 deletions lib/active_fedora/relation.rb
Original file line number Diff line number Diff line change
@@ -1,17 +1,39 @@
module ActiveFedora
class Relation
delegate :map, :each, :collect, :all?, :include?, :to => :to_a

include Delegation, SpawnMethods, QueryMethods, FinderMethods

attr_reader :loaded
attr_accessor :default_scoped
alias :loaded? :loaded

attr_accessor :limit_value, :where_values, :order_values

def initialize(klass)
attr_accessor :values, :klass

def initialize(klass, values = {})
@klass = klass
@loaded = false
self.where_values = []
self.order_values = []
@values = {}
end

# Tries to create a new record with the same scoped attributes
# defined in the relation. Returns the initialized object if validation fails.
#
# Expects arguments in the same format as +Base.create+.
#
# ==== Examples
# users = User.where(name: 'Oscar')
# users.create # #<User id: 3, name: "oscar", ...>
#
# users.create(name: 'fxn')
# users.create # #<User id: 4, name: "fxn", ...>
#
# users.create { |user| user.name = 'tenderlove' }
# # #<User id: 5, name: "tenderlove", ...>
#
# users.create(name: nil) # validation on name
# # #<User id: nil, name: nil, ...>
def create(*args, &block)
scoping { @klass.create(*args, &block) }
end

def reset
Expand Down Expand Up @@ -115,7 +137,7 @@ def find(*args)
if options.present?
options = args.first unless args.empty?
options = {conditions: options}
apply_finder_options(options).all
apply_finder_options(options)
else
raise ArgumentError, "#{self}.find() expects a pid. You provided `#{args.inspect}'" unless args.is_a? Array
find_with_ids(args, cast)
Expand Down Expand Up @@ -143,22 +165,13 @@ def find_some(ids, cast)
ids.map{|id| @klass.find_one(id, cast)}
end

# A convenience wrapper for <tt>find(:all, *args)</tt>. You can pass in all the
# same arguments to this method as you can to <tt>find(:all)</tt>.
def all(*args)
args.any? ? apply_finder_options(args.first).to_a : to_a
end



def to_a
return @records if loaded?
args = @klass == ActiveFedora::Base ? {:cast=>true} : {}
args[:rows] = @limit_value if @limit_value
args[:sort] = @order_values if @order_values
args[:rows] = limit_value if limit_value
args[:sort] = order_values if order_values

query = @where_values.present? ? @where_values : {}
@records = @klass.to_enum(:find_each, query, args).to_a
@records = @klass.to_enum(:find_each, where_values, args).to_a

@records
end
Expand All @@ -168,11 +181,10 @@ def to_a
def count(*args)
return apply_finder_options(args.first).count if args.any?
opts = {}
opts[:rows] = @limit_value if @limit_value
opts[:sort] = @order_values if @order_values
opts[:rows] = limit_value if limit_value
opts[:sort] = order_values if order_values

query = @where_values.present? ? @where_values : {}
@klass.calculate :count, query, opts
@klass.calculate :count, where_values, opts

end

Expand Down
Loading