active-fedora 9.10.0.pre1 → 9.10.0.pre2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +7 -0
- data/lib/active_fedora.rb +4 -0
- data/lib/active_fedora/associations/collection_association.rb +7 -7
- data/lib/active_fedora/attribute_assignment.rb +55 -0
- data/lib/active_fedora/attribute_methods.rb +149 -47
- data/lib/active_fedora/attribute_methods/read.rb +29 -44
- data/lib/active_fedora/attribute_methods/write.rb +16 -19
- data/lib/active_fedora/attributes.rb +5 -17
- data/lib/active_fedora/base.rb +2 -0
- data/lib/active_fedora/callbacks.rb +9 -9
- data/lib/active_fedora/common.rb +67 -0
- data/lib/active_fedora/core.rb +9 -22
- data/lib/active_fedora/errors.rb +29 -0
- data/lib/active_fedora/file.rb +5 -57
- data/lib/active_fedora/file_persistence.rb +27 -0
- data/lib/active_fedora/identifiable.rb +0 -5
- data/lib/active_fedora/indexing.rb +2 -2
- data/lib/active_fedora/inheritance.rb +34 -0
- data/lib/active_fedora/persistence.rb +22 -4
- data/lib/active_fedora/scoping.rb +80 -2
- data/lib/active_fedora/scoping/default.rb +6 -2
- data/lib/active_fedora/scoping/named.rb +141 -1
- data/lib/active_fedora/version.rb +1 -1
- data/spec/integration/collection_association_spec.rb +34 -3
- data/spec/integration/file_spec.rb +1 -1
- data/spec/integration/persistence_spec.rb +1 -1
- data/spec/unit/attributes_spec.rb +13 -7
- data/spec/unit/file_spec.rb +2 -2
- data/spec/unit/validations_spec.rb +1 -1
- metadata +6 -2
@@ -0,0 +1,27 @@
|
|
1
|
+
module ActiveFedora
|
2
|
+
module FilePersistence
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
include ActiveFedora::Persistence
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def _create_record(_options = {})
|
10
|
+
return false if content.nil?
|
11
|
+
ldp_source.content = content
|
12
|
+
ldp_source.create do |req|
|
13
|
+
req.headers.merge!(ldp_headers)
|
14
|
+
end
|
15
|
+
refresh
|
16
|
+
end
|
17
|
+
|
18
|
+
def _update_record(_options = {})
|
19
|
+
return true unless content_changed?
|
20
|
+
ldp_source.content = content
|
21
|
+
ldp_source.update do |req|
|
22
|
+
req.headers.merge!(ldp_headers)
|
23
|
+
end
|
24
|
+
refresh
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -59,14 +59,14 @@ module ActiveFedora
|
|
59
59
|
private
|
60
60
|
|
61
61
|
# index the record after it has been persisted to Fedora
|
62
|
-
def
|
62
|
+
def _create_record(options = {})
|
63
63
|
super
|
64
64
|
update_index if create_needs_index? && options.fetch(:update_index, true)
|
65
65
|
true
|
66
66
|
end
|
67
67
|
|
68
68
|
# index the record after it has been updated in Fedora
|
69
|
-
def
|
69
|
+
def _update_record(options = {})
|
70
70
|
super
|
71
71
|
update_index if update_needs_index? && options.fetch(:update_index, true)
|
72
72
|
true
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module ActiveFedora
|
2
|
+
module Inheritance
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
# Returns the class descending directly from ActiveFedora::Base, or
|
7
|
+
# an abstract class, if any, in the inheritance hierarchy.
|
8
|
+
#
|
9
|
+
# If A extends ActiveFedora::Base, A.base_class will return A. If B descends from A
|
10
|
+
# through some arbitrarily deep hierarchy, B.base_class will return A.
|
11
|
+
#
|
12
|
+
# If B < A and C < B and if A is an abstract_class then both B.base_class
|
13
|
+
# and C.base_class would return B as the answer since A is an abstract_class.
|
14
|
+
def base_class
|
15
|
+
return File if self == File || superclass == File
|
16
|
+
|
17
|
+
unless self <= Base
|
18
|
+
raise ActiveFedoraError, "#{name} doesn't belong in a hierarchy descending from ActiveFedora"
|
19
|
+
end
|
20
|
+
|
21
|
+
if self == Base || superclass == Base || superclass.abstract_class?
|
22
|
+
self
|
23
|
+
else
|
24
|
+
superclass.base_class
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Abstract classes can't have default scopes.
|
29
|
+
def abstract_class?
|
30
|
+
self == Base
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -33,7 +33,7 @@ module ActiveFedora
|
|
33
33
|
|
34
34
|
# Pushes the object and all of its new or dirty attached files into Fedora
|
35
35
|
def update(attributes)
|
36
|
-
|
36
|
+
assign_attributes(attributes)
|
37
37
|
save
|
38
38
|
end
|
39
39
|
|
@@ -70,6 +70,17 @@ module ActiveFedora
|
|
70
70
|
delete(*opts)
|
71
71
|
end
|
72
72
|
|
73
|
+
# Deletes the record in the database and freezes this instance to reflect
|
74
|
+
# that no changes should be made (since they can't be persisted).
|
75
|
+
#
|
76
|
+
# There's a series of callbacks associated with #destroy!. If the
|
77
|
+
# <tt>before_destroy</tt> callback throws +:abort+ the action is cancelled
|
78
|
+
# and #destroy! raises ActiveFedora::RecordNotDestroyed.
|
79
|
+
# See ActiveFedora::Callbacks for further details.
|
80
|
+
def destroy!
|
81
|
+
destroy || _raise_record_not_destroyed
|
82
|
+
end
|
83
|
+
|
73
84
|
def eradicate
|
74
85
|
self.class.eradicate(id)
|
75
86
|
end
|
@@ -138,12 +149,12 @@ module ActiveFedora
|
|
138
149
|
|
139
150
|
def create_or_update(*args)
|
140
151
|
raise ReadOnlyRecord if readonly?
|
141
|
-
result = new_record? ?
|
152
|
+
result = new_record? ? _create_record(*args) : _update_record(*args)
|
142
153
|
result != false
|
143
154
|
end
|
144
155
|
|
145
156
|
# Deals with preparing new object to be saved to Fedora, then pushes it and its attached files into Fedora.
|
146
|
-
def
|
157
|
+
def _create_record(_options = {})
|
147
158
|
assign_rdf_subject
|
148
159
|
serialize_attached_files
|
149
160
|
@ldp_source = @ldp_source.create
|
@@ -152,13 +163,20 @@ module ActiveFedora
|
|
152
163
|
refresh
|
153
164
|
end
|
154
165
|
|
155
|
-
def
|
166
|
+
def _update_record(_options = {})
|
156
167
|
serialize_attached_files
|
157
168
|
execute_sparql_update
|
158
169
|
save_contained_resources
|
159
170
|
refresh
|
160
171
|
end
|
161
172
|
|
173
|
+
def _raise_record_not_destroyed
|
174
|
+
@_association_destroy_exception ||= nil
|
175
|
+
raise @_association_destroy_exception || RecordNotDestroyed.new("Failed to destroy the record", self)
|
176
|
+
ensure
|
177
|
+
@_association_destroy_exception = nil
|
178
|
+
end
|
179
|
+
|
162
180
|
def refresh
|
163
181
|
@ldp_source = build_ldp_resource(id)
|
164
182
|
@resource = nil
|
@@ -1,6 +1,9 @@
|
|
1
|
+
require 'active_support/per_thread_registry'
|
2
|
+
|
1
3
|
module ActiveFedora
|
2
4
|
module Scoping
|
3
5
|
extend ActiveSupport::Concern
|
6
|
+
|
4
7
|
included do
|
5
8
|
include Default
|
6
9
|
include Named
|
@@ -8,11 +11,17 @@ module ActiveFedora
|
|
8
11
|
|
9
12
|
module ClassMethods
|
10
13
|
def current_scope #:nodoc:
|
11
|
-
|
14
|
+
ScopeRegistry.value_for(:current_scope, self)
|
12
15
|
end
|
13
16
|
|
14
17
|
def current_scope=(scope) #:nodoc:
|
15
|
-
|
18
|
+
ScopeRegistry.set_value_for(:current_scope, self, scope)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Collects attributes from scopes that should be applied when creating
|
22
|
+
# an AF instance for the particular class this is called on.
|
23
|
+
def scope_attributes # :nodoc:
|
24
|
+
all.scope_for_create
|
16
25
|
end
|
17
26
|
|
18
27
|
# Are there attributes associated with this scope?
|
@@ -20,5 +29,74 @@ module ActiveFedora
|
|
20
29
|
current_scope
|
21
30
|
end
|
22
31
|
end
|
32
|
+
|
33
|
+
def populate_with_current_scope_attributes # :nodoc:
|
34
|
+
return unless self.class.scope_attributes?
|
35
|
+
|
36
|
+
self.class.scope_attributes.each do |att, value|
|
37
|
+
send("#{att}=", value) if respond_to?("#{att}=")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def initialize_internals_callback # :nodoc:
|
42
|
+
super
|
43
|
+
populate_with_current_scope_attributes
|
44
|
+
end
|
45
|
+
|
46
|
+
# This class stores the +:current_scope+ and +:ignore_default_scope+ values
|
47
|
+
# for different classes. The registry is stored as a thread local, which is
|
48
|
+
# accessed through +ScopeRegistry.current+.
|
49
|
+
#
|
50
|
+
# This class allows you to store and get the scope values on different
|
51
|
+
# classes and different types of scopes. For example, if you are attempting
|
52
|
+
# to get the current_scope for the +Board+ model, then you would use the
|
53
|
+
# following code:
|
54
|
+
#
|
55
|
+
# registry = ActiveFedora::Scoping::ScopeRegistry
|
56
|
+
# registry.set_value_for(:current_scope, Board, some_new_scope)
|
57
|
+
#
|
58
|
+
# Now when you run:
|
59
|
+
#
|
60
|
+
# registry.value_for(:current_scope, Board)
|
61
|
+
#
|
62
|
+
# You will obtain whatever was defined in +some_new_scope+. The #value_for
|
63
|
+
# and #set_value_for methods are delegated to the current ScopeRegistry
|
64
|
+
# object, so the above example code can also be called as:
|
65
|
+
#
|
66
|
+
# ActiveFedora::Scoping::ScopeRegistry.set_value_for(:current_scope,
|
67
|
+
# Board, some_new_scope)
|
68
|
+
class ScopeRegistry # :nodoc:
|
69
|
+
extend ActiveSupport::PerThreadRegistry
|
70
|
+
|
71
|
+
VALID_SCOPE_TYPES = [:current_scope, :ignore_default_scope].freeze
|
72
|
+
|
73
|
+
def initialize
|
74
|
+
@registry = Hash.new { |hash, key| hash[key] = {} }
|
75
|
+
end
|
76
|
+
|
77
|
+
# Obtains the value for a given +scope_type+ and +model+.
|
78
|
+
def value_for(scope_type, model)
|
79
|
+
raise_invalid_scope_type!(scope_type)
|
80
|
+
klass = model
|
81
|
+
base = model.base_class
|
82
|
+
while klass <= base
|
83
|
+
value = @registry[scope_type][klass.name]
|
84
|
+
return value if value
|
85
|
+
klass = klass.superclass
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Sets the +value+ for a given +scope_type+ and +model+.
|
90
|
+
def set_value_for(scope_type, model, value)
|
91
|
+
raise_invalid_scope_type!(scope_type)
|
92
|
+
@registry[scope_type][model.name] = value
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def raise_invalid_scope_type!(scope_type)
|
98
|
+
raise ArgumentError, "Invalid scope type '#{scope_type}' sent to the registry. Scope types must be included in VALID_SCOPE_TYPES" unless VALID_SCOPE_TYPES.include?(scope_type)
|
99
|
+
end
|
100
|
+
end
|
23
101
|
end
|
24
102
|
end
|
@@ -46,6 +46,10 @@ module ActiveFedora
|
|
46
46
|
super || default_scopes.any? || respond_to?(:default_scope)
|
47
47
|
end
|
48
48
|
|
49
|
+
def before_remove_const #:nodoc:
|
50
|
+
self.current_scope = nil
|
51
|
+
end
|
52
|
+
|
49
53
|
protected
|
50
54
|
|
51
55
|
# Use this macro in your model to set a default scope for all operations on
|
@@ -125,11 +129,11 @@ module ActiveFedora
|
|
125
129
|
end
|
126
130
|
|
127
131
|
def ignore_default_scope? # :nodoc:
|
128
|
-
|
132
|
+
ScopeRegistry.value_for(:ignore_default_scope, base_class)
|
129
133
|
end
|
130
134
|
|
131
135
|
def ignore_default_scope=(ignore) # :nodoc:
|
132
|
-
|
136
|
+
ScopeRegistry.set_value_for(:ignore_default_scope, base_class, ignore)
|
133
137
|
end
|
134
138
|
|
135
139
|
# The ignore_default_scope flag is used to prevent an infinite recursion
|
@@ -16,7 +16,7 @@ module ActiveFedora
|
|
16
16
|
# fruits = fruits.limit(10) if limited?
|
17
17
|
#
|
18
18
|
# You can define a scope that applies to all finders using
|
19
|
-
# <tt>
|
19
|
+
# <tt>ActiveFedora::Base.default_scope</tt>.
|
20
20
|
def all
|
21
21
|
if current_scope
|
22
22
|
current_scope.clone
|
@@ -34,6 +34,146 @@ module ActiveFedora
|
|
34
34
|
relation
|
35
35
|
end
|
36
36
|
end
|
37
|
+
|
38
|
+
# Adds a class method for retrieving and querying objects.
|
39
|
+
# The method is intended to return an ActiveFedora::Relation
|
40
|
+
# object, which is composable with other scopes.
|
41
|
+
# If it returns nil or false, an
|
42
|
+
# {all}[rdoc-ref:Scoping::Named::ClassMethods#all] scope is returned instead.
|
43
|
+
#
|
44
|
+
# A \scope represents a narrowing of a database query, such as
|
45
|
+
# <tt>where(color: :red).select('shirts.*').includes(:washing_instructions)</tt>.
|
46
|
+
#
|
47
|
+
# class Shirt < ActiveFedora::Base
|
48
|
+
# scope :red, -> { where(color: 'red') }
|
49
|
+
# scope :dry_clean_only, -> { joins(:washing_instructions).where('washing_instructions.dry_clean_only = ?', true) }
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
# The above calls to #scope define class methods <tt>Shirt.red</tt> and
|
53
|
+
# <tt>Shirt.dry_clean_only</tt>. <tt>Shirt.red</tt>, in effect,
|
54
|
+
# represents the query <tt>Shirt.where(color: 'red')</tt>.
|
55
|
+
#
|
56
|
+
# You should always pass a callable object to the scopes defined
|
57
|
+
# with #scope. This ensures that the scope is re-evaluated each
|
58
|
+
# time it is called.
|
59
|
+
#
|
60
|
+
# Note that this is simply 'syntactic sugar' for defining an actual
|
61
|
+
# class method:
|
62
|
+
#
|
63
|
+
# class Shirt < ActiveFedora::Base
|
64
|
+
# def self.red
|
65
|
+
# where(color: 'red')
|
66
|
+
# end
|
67
|
+
# end
|
68
|
+
#
|
69
|
+
# Unlike <tt>Shirt.find(...)</tt>, however, the object returned by
|
70
|
+
# <tt>Shirt.red</tt> is not an Array but an ActiveFedora::Relation,
|
71
|
+
# which is composable with other scopes; it resembles the association object
|
72
|
+
# constructed by a {has_many}[rdoc-ref:Associations::ClassMethods#has_many]
|
73
|
+
# declaration. For instance, you can invoke <tt>Shirt.red.first</tt>, <tt>Shirt.red.count</tt>,
|
74
|
+
# <tt>Shirt.red.where(size: 'small')</tt>. Also, just as with the
|
75
|
+
# association objects, named \scopes act like an Array, implementing
|
76
|
+
# Enumerable; <tt>Shirt.red.each(&block)</tt>, <tt>Shirt.red.first</tt>,
|
77
|
+
# and <tt>Shirt.red.inject(memo, &block)</tt> all behave as if
|
78
|
+
# <tt>Shirt.red</tt> really was an array.
|
79
|
+
#
|
80
|
+
# These named \scopes are composable. For instance,
|
81
|
+
# <tt>Shirt.red.dry_clean_only</tt> will produce all shirts that are
|
82
|
+
# both red and dry clean only. Nested finds and calculations also work
|
83
|
+
# with these compositions: <tt>Shirt.red.dry_clean_only.count</tt>
|
84
|
+
# returns the number of garments for which these criteria obtain.
|
85
|
+
# Similarly with <tt>Shirt.red.dry_clean_only.average(:thread_count)</tt>.
|
86
|
+
#
|
87
|
+
# All scopes are available as class methods on the ActiveFedora::Base
|
88
|
+
# descendant upon which the \scopes were defined. But they are also
|
89
|
+
# available to {has_many}[rdoc-ref:Associations::ClassMethods#has_many]
|
90
|
+
# associations. If,
|
91
|
+
#
|
92
|
+
# class Person < ActiveFedora::Base
|
93
|
+
# has_many :shirts
|
94
|
+
# end
|
95
|
+
#
|
96
|
+
# then <tt>elton.shirts.red.dry_clean_only</tt> will return all of
|
97
|
+
# Elton's red, dry clean only shirts.
|
98
|
+
#
|
99
|
+
# \Named scopes can also have extensions, just as with
|
100
|
+
# {has_many}[rdoc-ref:Associations::ClassMethods#has_many] declarations:
|
101
|
+
#
|
102
|
+
# class Shirt < ActiveFedora::Base
|
103
|
+
# scope :red, -> { where(color: 'red') } do
|
104
|
+
# def dom_id
|
105
|
+
# 'red_shirts'
|
106
|
+
# end
|
107
|
+
# end
|
108
|
+
# end
|
109
|
+
#
|
110
|
+
# Scopes can also be used while creating/building a record.
|
111
|
+
#
|
112
|
+
# class Article < ActiveFedora::Base
|
113
|
+
# scope :published, -> { where(published: true) }
|
114
|
+
# end
|
115
|
+
#
|
116
|
+
# Article.published.new.published # => true
|
117
|
+
# Article.published.create.published # => true
|
118
|
+
#
|
119
|
+
# \Class methods on your model are automatically available
|
120
|
+
# on scopes. Assuming the following setup:
|
121
|
+
#
|
122
|
+
# class Article < ActiveFedora::Base
|
123
|
+
# scope :published, -> { where(published: true) }
|
124
|
+
# scope :featured, -> { where(featured: true) }
|
125
|
+
#
|
126
|
+
# def self.latest_article
|
127
|
+
# order('published_at desc').first
|
128
|
+
# end
|
129
|
+
#
|
130
|
+
# def self.titles
|
131
|
+
# pluck(:title)
|
132
|
+
# end
|
133
|
+
# end
|
134
|
+
#
|
135
|
+
# We are able to call the methods like this:
|
136
|
+
#
|
137
|
+
# Article.published.featured.latest_article
|
138
|
+
# Article.featured.titles
|
139
|
+
def scope(name, body, &block)
|
140
|
+
unless body.respond_to?(:call)
|
141
|
+
raise ArgumentError, 'The scope body needs to be callable.'
|
142
|
+
end
|
143
|
+
|
144
|
+
if dangerous_class_method?(name)
|
145
|
+
raise ArgumentError, "You tried to define a scope named \"#{name}\" " \
|
146
|
+
"on the model \"#{self.name}\", but Active Record already defined " \
|
147
|
+
"a class method with the same name."
|
148
|
+
end
|
149
|
+
|
150
|
+
valid_scope_name?(name)
|
151
|
+
extension = Module.new(&block) if block
|
152
|
+
|
153
|
+
if body.respond_to?(:to_proc)
|
154
|
+
singleton_class.send(:define_method, name) do |*args|
|
155
|
+
scope = all.scoping { instance_exec(*args, &body) }
|
156
|
+
scope = scope.extending(extension) if extension
|
157
|
+
|
158
|
+
scope || all
|
159
|
+
end
|
160
|
+
else
|
161
|
+
singleton_class.send(:define_method, name) do |*args|
|
162
|
+
scope = all.scoping { body.call(*args) }
|
163
|
+
scope = scope.extending(extension) if extension
|
164
|
+
|
165
|
+
scope || all
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
protected
|
171
|
+
|
172
|
+
def valid_scope_name?(name)
|
173
|
+
return unless respond_to?(name, true)
|
174
|
+
logger.warn "Creating scope :#{name}. " \
|
175
|
+
"Overwriting existing method #{self.name}.#{name}."
|
176
|
+
end
|
37
177
|
end
|
38
178
|
end
|
39
179
|
end
|
@@ -11,8 +11,6 @@ describe ActiveFedora::Base do
|
|
11
11
|
end
|
12
12
|
|
13
13
|
let(:library) { Library.create! }
|
14
|
-
let!(:book1) { Book.create!(library: library) }
|
15
|
-
let!(:book2) { Book.create!(library: library) }
|
16
14
|
|
17
15
|
after do
|
18
16
|
Object.send(:remove_const, :Library)
|
@@ -20,6 +18,9 @@ describe ActiveFedora::Base do
|
|
20
18
|
end
|
21
19
|
|
22
20
|
describe "load_from_solr" do
|
21
|
+
let!(:book1) { Book.create!(library: library) }
|
22
|
+
let!(:book2) { Book.create!(library: library) }
|
23
|
+
|
23
24
|
it "sets rows to count, if not specified" do
|
24
25
|
expect(library.books(response_format: :solr).size).to eq 2
|
25
26
|
end
|
@@ -35,6 +36,8 @@ describe ActiveFedora::Base do
|
|
35
36
|
end
|
36
37
|
|
37
38
|
describe "#delete_all" do
|
39
|
+
let!(:book1) { Book.create!(library: library) }
|
40
|
+
let!(:book2) { Book.create!(library: library) }
|
38
41
|
it "deletes em" do
|
39
42
|
expect {
|
40
43
|
library.books.delete_all
|
@@ -59,6 +62,8 @@ describe ActiveFedora::Base do
|
|
59
62
|
end
|
60
63
|
|
61
64
|
describe "#destroy_all" do
|
65
|
+
let!(:book1) { Book.create!(library: library) }
|
66
|
+
let!(:book2) { Book.create!(library: library) }
|
62
67
|
it "deletes em" do
|
63
68
|
expect {
|
64
69
|
library.books.destroy_all
|
@@ -67,6 +72,8 @@ describe ActiveFedora::Base do
|
|
67
72
|
end
|
68
73
|
|
69
74
|
describe "#find" do
|
75
|
+
let!(:book1) { Book.create!(library: library) }
|
76
|
+
let!(:book2) { Book.create!(library: library) }
|
70
77
|
it "finds the record that matches" do
|
71
78
|
expected = library.books.find(book1.id)
|
72
79
|
expect(expected).to eq book1
|
@@ -80,8 +87,11 @@ describe ActiveFedora::Base do
|
|
80
87
|
end
|
81
88
|
|
82
89
|
describe "#select" do
|
90
|
+
let!(:book1) { Book.create!(library: library) }
|
91
|
+
let!(:book2) { Book.create!(library: library) }
|
92
|
+
|
83
93
|
# TODO: Bug described in issue #609
|
84
|
-
xit "
|
94
|
+
xit "chooses a subset of objects in the relationship" do
|
85
95
|
expect(library.books.select([:id])).to include(book1.id)
|
86
96
|
end
|
87
97
|
it "works as a block" do
|
@@ -89,6 +99,27 @@ describe ActiveFedora::Base do
|
|
89
99
|
end
|
90
100
|
end
|
91
101
|
|
102
|
+
describe "#size" do
|
103
|
+
context "with associations in memory" do
|
104
|
+
context "and the association is already loaded" do
|
105
|
+
before do
|
106
|
+
library.books.to_a # force the association to be loaded
|
107
|
+
library.books.build
|
108
|
+
end
|
109
|
+
subject { library.books.size }
|
110
|
+
it { is_expected.to eq 1 }
|
111
|
+
end
|
112
|
+
|
113
|
+
context "and the association is not loaded" do
|
114
|
+
before do
|
115
|
+
library.books.build
|
116
|
+
end
|
117
|
+
subject { library.books.size }
|
118
|
+
it { is_expected.to eq 1 }
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
92
123
|
describe "finding the inverse" do
|
93
124
|
context "when no inverse exists" do
|
94
125
|
before do
|