/
is_data.rb
209 lines (181 loc) · 5.54 KB
/
is_data.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
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
# Shared code for a classes that are "data" sensu TaxonWorks (things like Projects, users, and preferences are not data).
#
# !! This module must in included last !!
#
module Shared::IsData
extend ActiveSupport::Concern
included do
include Pinnable
include Levenshtein
include Annotation
include Scopes
include Navigation
end
module ClassMethods
# @return [Boolean]
def is_community?
self < Shared::SharedAcrossProjects ? true : false
end
# @return [Array] of strings of only the non-cached and non-housekeeping column names
def data_attributes
column_names.reject { |c| %w{id project_id created_by_id updated_by_id created_at updated_at}
.include?(c) || c =~ /^cached/ }
end
# @return [Boolean]
# use update vs. a set of ids, but require the update to pass for all or none
def batch_update_attribute(ids: [], attribute: nil, value: nil)
return false if ids.empty? || attribute.nil? || value.nil?
begin
self.transaction do
self.where(id: ids).each do |li|
li.update(attribute => value)
end
end
rescue
return false
end
true
end
=begin
---------------------------
1 2 3 4 s returned for similar
---------------------------
1 |abc | C s si s i returned for identical
---------------------------
2 |abcd| s C s s C returned if class method, identical or similar
---------------------------
3 |abc | si s C s
---------------------------
4 | bc | C
---------------------------
=end
# @param [Hash] attr of matchable attributes
# @return [Scope]
def similar(attr)
klass = self
attr = Stripper.strip_similar_attributes(klass, attr)
attr = attr.select { |_kee, val| val.present? }
scope = klass.where(attr)
scope
end
# @param [Hash] attr of matchable attributes
# @return [Scope]
def identical(attr)
klass = self
attr = Stripper.strip_identical_attributes(klass, attr)
scope = klass.where(attr)
scope
end
end # END CLASS METHODS
# Returns whether it is permissible to try to destroy
# they record based on it's relationships to projects
# the user is in. I.e. false if it is related to data in
# a project in which they user is not a member.
# !! Does not look at :dependendant assertions
# @return [Boolean]
# true - there is at least some related data in another projects
# @param user [user_id or User]
# an id or User object
def is_destroyable?(user)
user = User.find(user) if !user.kind_of?(User)
return true if user.is_administrator?
p = user.projects.pluck(:id)
self.class.reflect_on_all_associations(:has_many).each do |r|
if r.klass.column_names.include?('project_id')
# If this has any related data in another project, we can't destroy it
# if !send(r.name).nil?
return false if send(r.name).where.not(project_id: p).count(:all) > 0
# end
end
end
self.class.reflect_on_all_associations(:has_one).each do |r|
if is_community? # *this* object is community, others we don't care about
if o = t.send(r.name)
return false if o.respond_to(:project_id) && !p.include?(o.project)
end
end
end
true
end
def is_editable?(user)
user = User.find(user) if !user.kind_of?(User)
return true if user.is_administrator? || is_community?
return false if !is_in_users_projects?(user)
true
end
def is_in_users_projects?(user)
user.projects.pluck(:id).include?(project_id)
end
# @return [Boolean]
# @params exclude [Array]
# of symbols
# @params only [Array]
# of symbols
# !! provide only exclude or only
def is_in_use?(exclude = [], only = [])
if only.empty?
self.class.reflect_on_all_associations(:has_many).each do |r|
next if exclude.include?(r.name)
return true if self.send(r.name).count(:all) > 0
end
self.class.reflect_on_all_associations(:has_one).each do |r|
next if exclude.include?(r.name)
return true if self.send(r.name).count(:all) > 0
end
else
only.each do |r|
return true if self.send(r.to_s).count(:all) > 0
end
end
false
end
# @return [Boolean]
def is_community?
self.class < Shared::SharedAcrossProjects ? true : false
end
# @return [Object]
# the same object, but namespaced to the base class
# used many places, might be good target for optimization
def metamorphosize
return self if self.class.descends_from_active_record?
self.becomes(self.class.base_class)
end
# @param [Symbol] keys
# @return [Hash]
def errors_excepting(*keys)
valid?
keys.each do |k|
errors.delete(k)
end
errors
end
# @return [Scope]
def similar
klass = self.class
attr = Stripper.strip_similar_attributes(klass, attributes)
# matching only those attributes from the instance which are not empty
attr = attr.select{ |_kee, val| val.present? }
if id
scope = klass.where(attr).not_self(self)
else
scope = klass.where(attr)
end
scope
end
# @return [Scope]
def identical
klass = self.class
attr = Stripper.strip_identical_attributes(klass, attributes)
if id
scope = klass.where(attr).not_self(self)
else
scope = klass.where(attr)
end
scope
end
# @param [Symbol] keys
# @return [Array]
def full_error_messages_excepting(*keys)
errors_excepting(*keys).full_messages
end
end