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
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
|
# File 'app/models/graph_starter/asset.rb', line 265
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
438
439
440
|
# File 'app/models/graph_starter/asset.rb', line 438
def self.authorized_associations
@authorized_associations ||= associations.except(*Asset.associations.keys + [:images, :image])
end
|
.authorized_for(user) ⇒ Object
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
|
# File 'app/models/graph_starter/asset.rb', line 376
def self.authorized_for(user)
require 'graph_starter/query_authorizer'
query, var, sec_var = if category_associations.size > 0
query = all(:asset).query
category_associations
relationship_types_cypher = category_associations.map {|name| self.associations[name].relationship_type }.join('|:')
query = query.optional_match("(asset)-[:#{relationship_types_cypher}]-(category:Asset)")
[query,
:asset, [:category]]
else
[all(:asset),
:asset]
end
::GraphStarter::QueryAuthorizer.new(query, asset: GraphStarter.configuration.scope_filters[self.name.to_sym])
.authorized_query(var, sec_var, user)
.with('DISTINCT asset AS asset, level')
.break
.proxy_as(self, :asset)
end
|
.authorized_properties(user) ⇒ Object
402
403
404
|
# File 'app/models/graph_starter/asset.rb', line 402
def self.authorized_properties(user)
authorized_properties_query(user).pluck(:property)
end
|
.authorized_properties_and_levels(user) ⇒ Object
406
407
408
|
# File 'app/models/graph_starter/asset.rb', line 406
def self.authorized_properties_and_levels(user)
authorized_properties_query(user).pluck(:property, :level)
end
|
.authorized_properties_query(user) ⇒ Object
410
411
412
413
414
415
416
417
418
419
420
421
422
|
# File 'app/models/graph_starter/asset.rb', line 410
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
200
201
202
203
204
205
206
207
208
209
|
# File 'app/models/graph_starter/asset.rb', line 200
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
211
212
213
|
# File 'app/models/graph_starter/asset.rb', line 211
def self.body_property?(property_name)
@body_property && @body_property.to_sym == property_name.to_sym
end
|
.category_associations(*association_names) ⇒ Object
98
99
100
101
102
103
104
105
106
107
108
109
|
# File 'app/models/graph_starter/asset.rb', line 98
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
215
216
217
218
219
220
221
|
# File 'app/models/graph_starter/asset.rb', line 215
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
181
182
183
184
185
186
187
|
# File 'app/models/graph_starter/asset.rb', line 181
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
363
364
365
366
|
# File 'app/models/graph_starter/asset.rb', line 363
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
224
225
226
227
228
229
230
|
# File 'app/models/graph_starter/asset.rb', line 224
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
232
233
234
|
# File 'app/models/graph_starter/asset.rb', line 232
def self.display_property?(property_name)
display_properties.nil? || display_properties.include?(property_name.to_sym)
end
|
.enumerable_property(property_name, values) ⇒ Object
133
134
135
136
137
138
139
140
|
# File 'app/models/graph_starter/asset.rb', line 133
def self.enumerable_property(property_name, values)
fail "values needs to be an Array, was #{values.inspect}" if !values.is_a?(Array)
validates property_name.to_sym, 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
147
148
149
|
# File 'app/models/graph_starter/asset.rb', line 147
def self.enumerable_property_values
@enumerable_property_values ||= {}
end
|
.enumerable_property_values_for(property_name) ⇒ Object
142
143
144
145
|
# File 'app/models/graph_starter/asset.rb', line 142
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_category(node_var, category_slug) ⇒ Object
122
123
124
125
126
127
128
129
130
|
# File 'app/models/graph_starter/asset.rb', line 122
def self.for_category(node_var, category_slug)
string = category_associations.map do |association_name|
association = GraphGist.associations[association_name]
"(#{node_var}#{association.arrow_cypher}(:#{association.target_class.mapped_label_name} {slug: {category_slug}}))"
end.join(' OR ')
all.where(string, category_slug: category_slug)
end
|
.for_query(query) ⇒ Object
294
295
296
297
298
299
300
301
302
|
# File 'app/models/graph_starter/asset.rb', line 294
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
57
58
59
60
|
# File 'app/models/graph_starter/asset.rb', line 57
def self.has_image
@has_image = true
has_one :out, :image, type: :HAS_IMAGE, model_class: '::GraphStarter::Image'
end
|
.has_image? ⇒ Boolean
66
67
68
|
# File 'app/models/graph_starter/asset.rb', line 66
def self.has_image?
!!@has_image
end
|
.has_images ⇒ Object
52
53
54
55
|
# File 'app/models/graph_starter/asset.rb', line 52
def self.has_images
@has_images = true
has_many :out, :images, type: :HAS_IMAGE, model_class: '::GraphStarter::Image'
end
|
.has_images? ⇒ Boolean
62
63
64
|
# File 'app/models/graph_starter/asset.rb', line 62
def self.has_images?
!!@has_images
end
|
.hidden_json_properties(*property_names) ⇒ Object
237
238
239
240
241
242
243
|
# File 'app/models/graph_starter/asset.rb', line 237
def self.hidden_json_properties(*property_names)
if property_names.empty?
@hidden_json_properties || []
else
@hidden_json_properties = property_names.map(&:to_sym)
end
end
|
.icon_class ⇒ Object
442
443
444
|
# File 'app/models/graph_starter/asset.rb', line 442
def self.icon_class
GraphStarter.configuration.icon_classes[self.name.to_sym]
end
|
.image_association ⇒ Object
70
71
72
73
74
75
76
|
# File 'app/models/graph_starter/asset.rb', line 70
def self.image_association
if has_images?
:images
elsif has_image?
:image
end
end
|
.json_methods(*method_names) ⇒ Object
246
247
248
249
250
251
252
|
# File 'app/models/graph_starter/asset.rb', line 246
def self.json_methods(*method_names)
if method_names.empty?
@json_methods || []
else
@json_methods = method_names.map(&:to_sym)
end
end
|
.model_slug ⇒ Object
368
369
370
|
# File 'app/models/graph_starter/asset.rb', line 368
def self.model_slug
name.tableize
end
|
.name_property(property_name = nil) ⇒ Object
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
|
# File 'app/models/graph_starter/asset.rb', line 161
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
177
178
179
|
# File 'app/models/graph_starter/asset.rb', line 177
def self.name_property?(property_name)
@name_property && @name_property.to_sym == property_name.to_sym
end
|
.properties ⇒ Object
372
373
374
|
# File 'app/models/graph_starter/asset.rb', line 372
def self.properties
attributes.keys - Asset.attributes.keys
end
|
.property_name_and_uuid_and_ruby_type_query ⇒ Object
424
425
426
427
428
429
430
431
432
433
434
435
436
|
# File 'app/models/graph_starter/asset.rb', line 424
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
152
153
154
|
# File 'app/models/graph_starter/asset.rb', line 152
def self.rated
@rated = true
end
|
.rated? ⇒ Boolean
156
157
158
|
# File 'app/models/graph_starter/asset.rb', line 156
def self.rated?
!!@rated
end
|
.sanitize_title(title) ⇒ Object
193
194
195
196
197
|
# File 'app/models/graph_starter/asset.rb', line 193
def self.sanitize_title(title)
sanitizer = Rails::Html::WhiteListSanitizer.new
sanitizer.sanitize(title, tags: %w(b em i strong)).try(:html_safe)
end
|
.search_properties(*array) ⇒ Object
286
287
288
289
290
291
292
|
# File 'app/models/graph_starter/asset.rb', line 286
def self.search_properties(*array)
if array.empty?
@search_properties || [name_property]
else
@search_properties = array
end
end
|
.unique_slug_from(string) ⇒ Object
26
27
28
29
30
31
32
33
34
35
36
37
38
|
# File 'app/models/graph_starter/asset.rb', line 26
def self.unique_slug_from(string)
if string.present?
slug = string.to_slug.normalize.to_s
while where(slug: slug).count > 0
if slug.match(/-\d+$/)
slug.gsub!(/-(\d+)$/) { "-#{$1.to_i + 1}" }
else
slug += '-2'
end
end
slug
end
end
|
Instance Method Details
#as_json(options = {}) ⇒ Object
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
|
# File 'app/models/graph_starter/asset.rb', line 328
def as_json(options = {})
data = {
id: id,
title: title,
name: title,
slug: slug,
model_slug: self.class.model_slug,
summary: summary,
categories: categories }.tap do |result|
result[:image_urls] = image_array.map(&:source_url) if image_array
result[:images] = images.map {|image| image.source.url } if self.class.has_images?
result[:image] = image.source_url if self.class.has_image? && image
self.class.category_associations.each do |association_name|
result[association_name] = send(association_name)
result[association_name].uniq! if result[association_name] && result[association_name].respond_to?(:to_a)
end
end
options[:root] ? {self.class.model_slug.singularize => data} : data
end
|
#categories ⇒ Object
111
112
113
114
115
116
117
118
119
|
# File 'app/models/graph_starter/asset.rb', line 111
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
78
79
80
81
82
83
84
|
# File 'app/models/graph_starter/asset.rb', line 78
def first_image
if self.class.has_images?
images.first
elsif self.class.has_image?
image
end
end
|
#first_image_source_url ⇒ Object
94
95
96
|
# File 'app/models/graph_starter/asset.rb', line 94
def first_image_source_url
first_image && first_image.source_url
end
|
#image_array ⇒ Object
86
87
88
89
90
91
92
|
# File 'app/models/graph_starter/asset.rb', line 86
def image_array
if self.class.has_images?
images.to_a
elsif self.class.has_image?
[image].compact
end
end
|
#place_slug ⇒ Object
20
21
22
23
24
|
# File 'app/models/graph_starter/asset.rb', line 20
def place_slug
return if self.slug.present?
self.slug = self.class.unique_slug_from(safe_title)
end
|
#rating_for(user) ⇒ Object
261
262
263
|
# File 'app/models/graph_starter/asset.rb', line 261
def rating_for(user)
rated_by_user(nil, :rating).where(uuid: user.uuid).pluck(:rating)[0]
end
|
#rating_level_for(user) ⇒ Object
256
257
258
259
|
# File 'app/models/graph_starter/asset.rb', line 256
def rating_level_for(user)
rating = rating_for(user)
rating && rating.level
end
|
#safe_title ⇒ Object
189
190
191
|
# File 'app/models/graph_starter/asset.rb', line 189
def safe_title
self.class.sanitize_title(title)
end
|
#secret_sauce_recommendations ⇒ Object
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
|
# File 'app/models/graph_starter/asset.rb', line 304
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
355
356
357
|
# File 'app/models/graph_starter/asset.rb', line 355
def total_view_count
views.map(&:count).sum
end
|
#unique_view_count ⇒ Object
359
360
361
|
# File 'app/models/graph_starter/asset.rb', line 359
def unique_view_count
views.size
end
|
#views ⇒ Object
351
352
353
|
# File 'app/models/graph_starter/asset.rb', line 351
def views
@views ||= viewer_sessions.rels
end
|