Class: GraphStarter::Asset
- Inherits:
-
Object
- Object
- GraphStarter::Asset
show all
- Includes:
- Authorizable, Neo4j::ActiveNode, Neo4j::Timestamps
- Defined in:
- app/models/graph_starter/asset.rb
Defined Under Namespace
Classes: SecretSauceRecommendation
Class Method Summary
collapse
Instance Method Summary
collapse
#set_access_levels
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method_name, *args, &block) ⇒ Object
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
|
# File 'app/models/graph_starter/asset.rb', line 203
def method_missing(method_name, *args, &block)
if [:name, :title].include?(method_name.to_sym)
self.class.send(:define_method, method_name) do
read_attribute(self.class.name_property)
end
send(method_name)
elsif method_name.to_sym == :body
if self.class.body_property
self.class.send(:define_method, method_name) do
read_attribute(self.class.body_property)
end
send(method_name)
end
else
super
end
end
|
Class Method Details
.authorized_associations ⇒ Object
370
371
372
|
# File 'app/models/graph_starter/asset.rb', line 370
def self.authorized_associations
@authorized_associations ||= associations.except(*Asset.associations.keys + [:images, :image])
end
|
.authorized_for(user) ⇒ Object
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
|
# File 'app/models/graph_starter/asset.rb', line 307
def self.authorized_for(user)
require 'graph_starter/query_authorizer'
query, associations = if category_associations.size > 0
query = all(:asset).query
query = category_associations.each_with_index.inject(query) do |query, (association_name, i)|
category_association = self.associations[association_name]
clause = "(asset)#{category_association.arrow_cypher}(category:#{category_association.target_class})"
query.optional_match(clause).with(:asset, "#{"categories#{i-1} +" if i > 0} collect(category) AS categories#{i}")
end.unwind(category: "categories#{category_associations.size - 1}")
[query,
[:asset, :category]]
else
[all(:asset),
:asset]
end
::GraphStarter::QueryAuthorizer.new(query, asset: GraphStarter.configuration.scope_filters[self.name.to_sym])
.authorized_query(associations, user)
.with('DISTINCT asset AS asset, level')
.break
.proxy_as(self, :asset)
end
|
.authorized_properties(user) ⇒ Object
334
335
336
|
# File 'app/models/graph_starter/asset.rb', line 334
def self.authorized_properties(user)
authorized_properties_query(user).pluck(:property)
end
|
.authorized_properties_and_levels(user) ⇒ Object
338
339
340
|
# File 'app/models/graph_starter/asset.rb', line 338
def self.authorized_properties_and_levels(user)
authorized_properties_query(user).pluck(:property, :level)
end
|
.authorized_properties_query(user) ⇒ Object
342
343
344
345
346
347
348
349
350
351
352
353
354
|
# File 'app/models/graph_starter/asset.rb', line 342
def self.authorized_properties_query(user)
query = property_name_and_uuid_and_ruby_type_query
.merge(model: {Model: {name: name}})
.on_create_set(model: {private: false})
.break
.merge('model-[:HAS_PROPERTY]->(property:Property {name: property_name})')
.on_create_set(property: {private: false})
.on_create_set('property.uuid = uuid, property.ruby_type = ruby_type')
.with(:property)
::GraphStarter::Property
QueryAuthorizer.new(query).authorized_query(:property, user)
end
|
.body_property(property_name = nil) ⇒ Object
157
158
159
160
161
162
163
164
165
166
|
# File 'app/models/graph_starter/asset.rb', line 157
def self.body_property(property_name = nil)
if property_name.nil?
@body_property
else
fail "Cannot declare body_property twice" if @body_property.present?
name = property_name.to_sym
fail ArgumentError, "Property #{name} is not defined" if !attributes.key?(name.to_s)
@body_property = name
end
end
|
.body_property?(property_name) ⇒ Boolean
168
169
170
|
# File 'app/models/graph_starter/asset.rb', line 168
def self.body_property?(property_name)
@body_property && @body_property.to_sym == property_name.to_sym
end
|
.category_associations(*association_names) ⇒ Object
71
72
73
74
75
76
77
78
79
80
81
82
|
# File 'app/models/graph_starter/asset.rb', line 71
def self.category_associations(*association_names)
if association_names.empty?
@category_associations || []
else
fail "Cannot declare category_associations twice" if @category_associations.present?
names = association_names.map(&:to_sym)
bad_names = names.select {|name| associations[name].nil? }
fail ArgumentError, "Associations #{bad_names.join(', ')} is not defined" if !bad_names.empty?
@category_associations = names
end
end
|
.default_body_property ⇒ Object
172
173
174
175
176
177
178
|
# File 'app/models/graph_starter/asset.rb', line 172
def self.default_body_property
if @body_property.nil? && !attributes.key?('body')
fail "No body_property defined for #{self.name}!"
end
@body_property
end
|
.default_name_property ⇒ Object
143
144
145
146
147
148
149
|
# File 'app/models/graph_starter/asset.rb', line 143
def self.default_name_property
(%w(name title) & attributes.keys)[0].tap do |property|
if property.nil?
fail "No name_property defined for #{self.name}!"
end
end
end
|
.descendants ⇒ Object
294
295
296
297
|
# File 'app/models/graph_starter/asset.rb', line 294
def self.descendants
Rails.application.eager_load! if Rails.env == 'development'
Neo4j::ActiveNode::Labels._wrapped_classes.select { |klass| klass < self }
end
|
.display_properties(*property_names) ⇒ Object
181
182
183
184
185
186
187
|
# File 'app/models/graph_starter/asset.rb', line 181
def self.display_properties(*property_names)
if property_names.empty?
@display_properties
else
@display_properties = property_names.map(&:to_sym)
end
end
|
.display_property?(property_name) ⇒ Boolean
189
190
191
|
# File 'app/models/graph_starter/asset.rb', line 189
def self.display_property?(property_name)
display_properties.nil? || display_properties.include?(property_name.to_sym)
end
|
.enumerable_property(property_name, values) ⇒ Object
95
96
97
98
99
100
101
102
|
# File 'app/models/graph_starter/asset.rb', line 95
def self.enumerable_property(property_name, values)
fail "values needs to be an Array, was #{values.inspect}" if !values.is_a?(Array)
validates :status, inclusion: {in: values}
enumerable_property_values[self.name.to_sym] ||= {}
enumerable_property_values[self.name.to_sym][property_name.to_sym] ||= values
end
|
.enumerable_property_values ⇒ Object
109
110
111
|
# File 'app/models/graph_starter/asset.rb', line 109
def self.enumerable_property_values
@enumerable_property_values ||= {}
end
|
.enumerable_property_values_for(property_name) ⇒ Object
104
105
106
107
|
# File 'app/models/graph_starter/asset.rb', line 104
def self.enumerable_property_values_for(property_name)
enumerable_property_values[self.name.to_sym] &&
enumerable_property_values[self.name.to_sym][property_name.to_sym]
end
|
.for_query(query) ⇒ Object
232
233
234
235
236
237
238
239
240
|
# File 'app/models/graph_starter/asset.rb', line 232
def self.for_query(query)
where_clause = self.search_properties.map do |property|
fail "Invalid property: #{property}" if attributes[property].nil?
"asset.#{property} =~ {query}"
end.join(' OR ')
query_string = query.strip.gsub(/\s+/, '.*')
all(:asset).where(where_clause).params(query: "(?i).*#{query_string}.*")
end
|
.has_image ⇒ Object
30
31
32
33
|
# File 'app/models/graph_starter/asset.rb', line 30
def self.has_image
@has_image = true
has_one :out, :image, type: :HAS_IMAGE, model_class: '::GraphStarter::Image'
end
|
.has_image? ⇒ Boolean
39
40
41
|
# File 'app/models/graph_starter/asset.rb', line 39
def self.has_image?
!!@has_image
end
|
.has_images ⇒ Object
25
26
27
28
|
# File 'app/models/graph_starter/asset.rb', line 25
def self.has_images
@has_images = true
has_many :out, :images, type: :HAS_IMAGE, model_class: '::GraphStarter::Image'
end
|
.has_images? ⇒ Boolean
35
36
37
|
# File 'app/models/graph_starter/asset.rb', line 35
def self.has_images?
!!@has_images
end
|
.icon_class ⇒ Object
374
375
376
|
# File 'app/models/graph_starter/asset.rb', line 374
def self.icon_class
GraphStarter.configuration.icon_classes[self.name.to_sym]
end
|
.image_association ⇒ Object
43
44
45
46
47
48
49
|
# File 'app/models/graph_starter/asset.rb', line 43
def self.image_association
if has_images?
:images
elsif has_image?
:image
end
end
|
.model_slug ⇒ Object
299
300
301
|
# File 'app/models/graph_starter/asset.rb', line 299
def self.model_slug
name.tableize
end
|
.name_property(property_name = nil) ⇒ Object
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
|
# File 'app/models/graph_starter/asset.rb', line 123
def self.name_property(property_name = nil)
if property_name.nil?
name_property(default_name_property) if @name_property.nil?
@name_property
else
fail "Cannot declare name_property twice" if @name_property.present?
name = property_name.to_sym
fail ArgumentError, "Property #{name} is not defined" if !attributes.key?(name.to_s)
@name_property = name
validates name, presence: true
index name
end
end
|
.name_property?(property_name) ⇒ Boolean
139
140
141
|
# File 'app/models/graph_starter/asset.rb', line 139
def self.name_property?(property_name)
@name_property && @name_property.to_sym == property_name.to_sym
end
|
.properties ⇒ Object
303
304
305
|
# File 'app/models/graph_starter/asset.rb', line 303
def self.properties
attributes.keys - Asset.attributes.keys
end
|
.property_name_and_uuid_and_ruby_type_query ⇒ Object
356
357
358
359
360
361
362
363
364
365
366
367
368
|
# File 'app/models/graph_starter/asset.rb', line 356
def self.property_name_and_uuid_and_ruby_type_query
properties_and_uuids_and_ruby_types = properties.map do |property|
type = self.attributes[property][:type]
type = type.name if type.is_a?(Class)
[property, SecureRandom.uuid, type]
end
Neo4j::Session.current.query
.with('{array} AS array')
.unwind('array AS row')
.params(array: properties_and_uuids_and_ruby_types)
.with('row[0] AS property_name, row[1] AS uuid, row[2] AS ruby_type')
end
|
.rated ⇒ Object
114
115
116
|
# File 'app/models/graph_starter/asset.rb', line 114
def self.rated
@rated = true
end
|
.rated? ⇒ Boolean
118
119
120
|
# File 'app/models/graph_starter/asset.rb', line 118
def self.rated?
!!@rated
end
|
.search_properties(*array) ⇒ Object
224
225
226
227
228
229
230
|
# File 'app/models/graph_starter/asset.rb', line 224
def self.search_properties(*array)
if array.empty?
@search_properties || [name_property]
else
@search_properties = array
end
end
|
Instance Method Details
#as_json(options = {}) ⇒ Object
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
|
# File 'app/models/graph_starter/asset.rb', line 266
def as_json(options = {})
data = {
id: id,
title: title,
name: title,
model_slug: self.class.model_slug,
summary: summary,
image_urls: @asset.reload.image_array.map(&:source_url)
}.tap do |result|
result[:images] = images.map {|image| image.source.url } if self.class.has_images?
result[:image] = image.source_url if self.class.has_image? && image
end
options[:root] ? {self.class.model_slug.singularize => data} : data
end
|
#categories ⇒ Object
84
85
86
87
88
89
90
91
92
|
# File 'app/models/graph_starter/asset.rb', line 84
def categories
if self.class.category_associations
self.class.category_associations.flat_map do |category_association|
send(category_association)
end.compact
else
[]
end
end
|
#first_image ⇒ Object
51
52
53
54
55
56
57
|
# File 'app/models/graph_starter/asset.rb', line 51
def first_image
if self.class.has_images?
images.first
elsif self.class.has_image?
image
end
end
|
#first_image_source_url ⇒ Object
67
68
69
|
# File 'app/models/graph_starter/asset.rb', line 67
def first_image_source_url
first_image && first_image.source_url
end
|
#image_array ⇒ Object
59
60
61
62
63
64
65
|
# File 'app/models/graph_starter/asset.rb', line 59
def image_array
if self.class.has_images?
images.to_a
elsif self.class.has_image?
[image].compact
end
end
|
#rating_for(user) ⇒ Object
199
200
201
|
# File 'app/models/graph_starter/asset.rb', line 199
def rating_for(user)
rated_by_user(nil, :rating).where(uuid: user.uuid).pluck(:rating)[0]
end
|
#rating_level_for(user) ⇒ Object
194
195
196
197
|
# File 'app/models/graph_starter/asset.rb', line 194
def rating_level_for(user)
rating = rating_for(user)
rating && rating.level
end
|
#safe_title ⇒ Object
151
152
153
154
155
|
# File 'app/models/graph_starter/asset.rb', line 151
def safe_title
sanitizer = Rails::Html::WhiteListSanitizer.new
sanitizer.sanitize(title, tags: %w(b em i strong)).try(:html_safe)
end
|
#secret_sauce_recommendations ⇒ Object
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
|
# File 'app/models/graph_starter/asset.rb', line 242
def secret_sauce_recommendations
user_class = GraphStarter.configuration.user_class
return [] if user_class.nil?
user_class = (user_class.is_a?(Class) ? user_class : user_class.to_s.constantize)
user_label = user_class.mapped_label_name
query_as(:source)
.match('source-[:HAS_CATEGORY]->(category:Category)<-[:HAS_CATEGORY]-(asset:Asset)')
.break
.optional_match("source<-[:CREATED]-(creator:#{user_label})-[:CREATED]->asset")
.break
.optional_match("source<-[:VIEWED]-(viewer:#{user_label})-[:VIEWED]->asset")
.limit(5)
.order('score DESC')
.pluck(
:asset,
'(count(category) * 2) +
(count(creator) * 4) +
(count(viewer) * 0.1) AS score').map do |other_asset, score|
SecretSauceRecommendation.new(other_asset, score)
end
end
|
#total_view_count ⇒ Object
286
287
288
|
# File 'app/models/graph_starter/asset.rb', line 286
def total_view_count
views.map(&:count).sum
end
|
#unique_view_count ⇒ Object
290
291
292
|
# File 'app/models/graph_starter/asset.rb', line 290
def unique_view_count
views.size
end
|
#views ⇒ Object
282
283
284
|
# File 'app/models/graph_starter/asset.rb', line 282
def views
@views ||= viewer_sessions.rels
end
|