-
Notifications
You must be signed in to change notification settings - Fork 63
/
relation.rb
181 lines (157 loc) · 5.1 KB
/
relation.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
module ActiveFedora
class Relation
include Enumerable
include FinderMethods, Calculations, SpawnMethods, QueryMethods, Delegation
attr_reader :loaded
attr_accessor :default_scoped
alias loaded? loaded
attr_accessor :values, :klass
def initialize(klass, _values = {})
@klass = klass
@loaded = false
@values = {}
end
# This method gets called on clone
def initialize_copy(_other)
# Dup the values
@values = Hash[@values]
reset
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
@first = @loaded = nil
@records = []
self
end
def to_a
return @records if loaded?
args = @klass == ActiveFedora::Base ? { cast: true } : {}
args[:rows] = limit_value if limit_value
args[:start] = offset_value if offset_value
args[:sort] = order_values if order_values
@records = to_enum(:find_each, where_values, args).to_a
@loaded = true
@records
end
# Scope all queries to the current scope.
#
# Comment.where(post_id: 1).scoping do
# Comment.first
# end
# # => SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = 1 ORDER BY "comments"."id" ASC LIMIT 1
#
# Please check unscoped if you want to remove all previous scopes (including
# the default_scope) during the execution of a block.
def scoping
previous = klass.current_scope
klass.current_scope = self
yield
ensure
klass.current_scope = previous
end
def ==(other)
case other
when Relation
other.where_values == where_values
when Array
to_a == other
end
end
delegate :inspect, to: :to_a
# Destroys the records matching +conditions+ by instantiating each
# record and calling its +destroy+ method. Each object's callbacks are
# executed (including <tt>:dependent</tt> association options and
# +before_destroy+/+after_destroy+ Observer methods). Returns the
# collection of objects that were destroyed; each will be frozen, to
# reflect that no changes should be made (since they can't be
# persisted).
#
# Note: Instantiation, callback execution, and deletion of each
# record can be time consuming when you're removing many records at
# once. It generates at least one fedora +DELETE+ query per record (or
# possibly more, to enforce your callbacks). If you want to delete many
# rows quickly, without concern for their associations or callbacks, use
# +delete_all+ instead.
#
# ==== Parameters
#
# * +conditions+ - A string, array, or hash that specifies which records
# to destroy. If omitted, all records are destroyed. See the
# Conditions section in the ActiveFedora::Relation#where for
# more information.
#
# ==== Examples
#
# Person.destroy_all(:status_s => "inactive")
# Person.where(:age_i => 18).destroy_all
def destroy_all(conditions = nil)
if conditions
where(conditions).destroy_all
else
to_a.each(&:destroy).tap { reset }.size
end
end
def delete_all(conditions = nil)
if conditions
where(conditions).delete_all
else
to_a.each(&:delete).tap { reset }.size
end
end
# Returns a hash of where conditions.
#
# User.where(name: 'Oscar').where_values_hash
# # => {name: "Oscar"}
def where_values_hash
{}
end
def scope_for_create
@scope_for_create ||= where_values_hash.merge(create_with_value)
end
def each
if loaded?
@records.each { |item| yield item } if block_given?
@records.to_enum
else
find_each(where_values) { |item| yield item } if block_given?
enum_for(:find_each, where_values)
end
end
def empty?
!any?
end
private
VALID_FIND_OPTIONS = [:order, :limit, :start, :conditions, :cast].freeze
def apply_finder_options(options)
relation = clone
return relation unless options
options.assert_valid_keys(VALID_FIND_OPTIONS)
finders = options.dup
finders.delete_if { |key, value| value.nil? && key != :limit }
([:order, :limit, :start] & finders.keys).each do |finder|
relation = relation.send(finder, finders[finder])
end
relation = relation.where(finders[:conditions]) if options.key?(:conditions)
relation
end
end
end