Class: GraphStarter::Asset
- Inherits:
-
Object
- Object
- GraphStarter::Asset
- Includes:
- Authorizable, Neo4j::ActiveNode, Neo4j::Timestamps
- Defined in:
- app/models/graph_starter/asset.rb
Defined Under Namespace
Classes: SecretSauceRecommendation
Class Method Summary collapse
- .authorized_associations ⇒ Object
- .authorized_for(user) ⇒ Object
- .authorized_properties(user) ⇒ Object
- .authorized_properties_and_levels(user) ⇒ Object
- .authorized_properties_query(user) ⇒ Object
- .body_property(property_name = nil) ⇒ Object
- .body_property?(property_name) ⇒ Boolean
- .category_associations(*association_names) ⇒ Object
- .default_body_property ⇒ Object
- .default_name_property ⇒ Object
- .descendants ⇒ Object
- .display_properties(*property_names) ⇒ Object
- .display_property?(property_name) ⇒ Boolean
- .enumerable_property(property_name, values) ⇒ Object
- .enumerable_property_values ⇒ Object
- .enumerable_property_values_for(property_name) ⇒ Object
- .for_query(query) ⇒ Object
- .has_image ⇒ Object
- .has_image? ⇒ Boolean
- .has_images ⇒ Object
- .has_images? ⇒ Boolean
- .icon_class ⇒ Object
- .image_association ⇒ Object
- .model_slug ⇒ Object
- .name_property(property_name = nil) ⇒ Object
- .name_property?(property_name) ⇒ Boolean
- .properties ⇒ Object
- .property_name_and_uuid_and_ruby_type_query ⇒ Object
- .rated ⇒ Object
- .rated? ⇒ Boolean
- .search_properties(*array) ⇒ Object
Instance Method Summary collapse
- #as_json(options = {}) ⇒ Object
- #categories ⇒ Object
- #first_image ⇒ Object
- #first_image_source_url ⇒ Object
- #image_array ⇒ Object
- #method_missing(method_name, *args, &block) ⇒ Object
- #rating_for(user) ⇒ Object
- #rating_level_for(user) ⇒ Object
- #safe_title ⇒ Object
- #secret_sauce_recommendations ⇒ Object
- #total_view_count ⇒ Object
- #unique_view_count ⇒ Object
- #views ⇒ Object
Methods included from Authorizable
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
374 375 376 |
# File 'app/models/graph_starter/asset.rb', line 374 def self. ||= associations.except(*Asset.associations.keys + [:images, :image]) end |
.authorized_for(user) ⇒ Object
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 333 334 335 336 |
# File 'app/models/graph_starter/asset.rb', line 308 def self.(user) require 'graph_starter/query_authorizer' query, associations = if category_associations.size > 0 query = all(:asset).query last_associations_var = "categories#{category_associations.size - 1}" 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 .with("asset, CASE #{last_associations_var} WHEN [] THEN [null] ELSE #{last_associations_var} END AS #{last_associations_var}") .unwind(category: last_associations_var) [query, [:asset, :category]] else [all(:asset), :asset] end ::GraphStarter::QueryAuthorizer.new(query, asset: GraphStarter.configuration.scope_filters[self.name.to_sym]) .(associations, user) .with('DISTINCT asset AS asset, level') .break .proxy_as(self, :asset) end |
.authorized_properties(user) ⇒ Object
338 339 340 |
# File 'app/models/graph_starter/asset.rb', line 338 def self.(user) (user).pluck(:property) end |
.authorized_properties_and_levels(user) ⇒ Object
342 343 344 |
# File 'app/models/graph_starter/asset.rb', line 342 def self.(user) (user).pluck(:property, :level) end |
.authorized_properties_query(user) ⇒ Object
346 347 348 349 350 351 352 353 354 355 356 357 358 |
# File 'app/models/graph_starter/asset.rb', line 346 def self.(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 # rubocop:disable Lint/Void QueryAuthorizer.new(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
295 296 297 298 |
# File 'app/models/graph_starter/asset.rb', line 295 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
378 379 380 |
# File 'app/models/graph_starter/asset.rb', line 378 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
300 301 302 |
# File 'app/models/graph_starter/asset.rb', line 300 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
304 305 306 |
# File 'app/models/graph_starter/asset.rb', line 304 def self.properties attributes.keys - Asset.attributes.keys end |
.property_name_and_uuid_and_ruby_type_query ⇒ Object
360 361 362 363 364 365 366 367 368 369 370 371 372 |
# File 'app/models/graph_starter/asset.rb', line 360 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 281 |
# File 'app/models/graph_starter/asset.rb', line 266 def as_json( = {}) data = { id: id, title: title, name: title, 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 end [: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 (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 (user) = (user) && .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? # Should fix this later 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
287 288 289 |
# File 'app/models/graph_starter/asset.rb', line 287 def total_view_count views.map(&:count).sum end |
#unique_view_count ⇒ Object
291 292 293 |
# File 'app/models/graph_starter/asset.rb', line 291 def unique_view_count views.size end |
#views ⇒ Object
283 284 285 |
# File 'app/models/graph_starter/asset.rb', line 283 def views @views ||= viewer_sessions.rels end |