naranya_ecm-sdk 0.0.14 → 0.0.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/lib/naranya_ecm/behaviors/localizable.rb +27 -31
  3. data/lib/naranya_ecm/behaviors/mediable.rb +25 -0
  4. data/lib/naranya_ecm/behaviors/timestampable.rb +18 -15
  5. data/lib/naranya_ecm/cache/key.rb +32 -56
  6. data/lib/naranya_ecm/cache/methods.rb +50 -63
  7. data/lib/naranya_ecm/lifecycles/content_lifecycle.rb +39 -16
  8. data/lib/naranya_ecm/models/category.rb +11 -30
  9. data/lib/naranya_ecm/models/content.rb +73 -73
  10. data/lib/naranya_ecm/models/content_version.rb +50 -29
  11. data/lib/naranya_ecm/models/download_authorization.rb +22 -26
  12. data/lib/naranya_ecm/models/media_resource.rb +49 -15
  13. data/lib/naranya_ecm/rest/associations.rb +156 -0
  14. data/lib/naranya_ecm/rest/client.rb +4 -0
  15. data/lib/naranya_ecm/rest/errors.rb +53 -0
  16. data/lib/naranya_ecm/rest/finder_methods.rb +50 -0
  17. data/lib/naranya_ecm/rest/model.rb +215 -0
  18. data/lib/naranya_ecm/rest/persistence.rb +122 -0
  19. data/lib/naranya_ecm/rest/relation.rb +54 -0
  20. data/lib/naranya_ecm/search/hit.rb +19 -14
  21. data/lib/naranya_ecm/search/methods.rb +18 -20
  22. data/lib/naranya_ecm/search/query.rb +229 -230
  23. data/lib/naranya_ecm/search/results.rb +136 -139
  24. data/lib/naranya_ecm-sdk/version.rb +1 -1
  25. data/lib/naranya_ecm-sdk.rb +54 -13
  26. data/naranya_ecm-sdk.gemspec +1 -1
  27. data/spec/models/category_spec.rb +7 -2
  28. data/spec/models/content_spec.rb +11 -2
  29. data/spec/models/media_spec.rb +1 -1
  30. data/spec/spec_helper.rb +1 -1
  31. data/spec/support/naranya_ecms_shared_specs.rb +0 -12
  32. metadata +15 -19
  33. data/lib/naranya_ecm/behaviors/resourceable.rb +0 -22
  34. data/lib/naranya_ecm/behaviors.rb +0 -10
  35. data/lib/naranya_ecm/cache.rb +0 -9
  36. data/lib/naranya_ecm/has_many_patch.rb +0 -105
  37. data/lib/naranya_ecm/lifecycles/lifecycleable.rb +0 -43
  38. data/lib/naranya_ecm/lifecycles/version_lifecycle.rb +0 -75
  39. data/lib/naranya_ecm/lifecycles.rb +0 -10
  40. data/lib/naranya_ecm/models/embedded_hash.rb +0 -10
  41. data/lib/naranya_ecm/models/embedded_localized_hash.rb +0 -38
  42. data/lib/naranya_ecm/models/lifecycle.rb +0 -34
  43. data/lib/naranya_ecm/models.rb +0 -15
  44. data/lib/naranya_ecm/search.rb +0 -14
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ab5a255c31cbc5ece70b173939a3d883331e2355
4
- data.tar.gz: aa2552ae589d918f381ca929784704e7119e5fcc
3
+ metadata.gz: 3f6a90236bd794d754bdf445805afdf1b4539d4f
4
+ data.tar.gz: e40ad3616ae04fb8932e2df7fb47546efb0ce7b1
5
5
  SHA512:
6
- metadata.gz: 7329e2ad01e89a35391762dc82923953bf813de13ff4378de9dd59a696803d36d02920f74db48cb311c42da8ba8f22ccccde0344fcbfd1dd45ad2f5a7952c3cb
7
- data.tar.gz: c462661938efd0c6f57dc24822500374f46c7f5368df99a8440d3479df1699f91582151dddb0eb89b863a0792c45152a6b3d4d5424e81d871f75a58f65fb3345
6
+ metadata.gz: f0802bf180226d3b443697ab502f09e1b13a23dd79105b246419de9a8e534cdc4db8351b6577953551ad3e8361c3a67a61e35c896509266c2309affbb9458f3a
7
+ data.tar.gz: 62565940d9f2025491f2a5d30d45990e1a2744afa2b411feb8c0bd1ebb5b766a4bd92b87b0127a22e098e6619686379318762f725dcabc8f163f86443316fc9b
@@ -1,41 +1,37 @@
1
1
  require 'active_support/concern'
2
2
 
3
- module NaranyaEcm
3
+ module NaranyaEcm::Behaviors
4
+ module Localizable
4
5
 
5
- module Behaviors
6
- module Localizable
6
+ extend ActiveSupport::Concern
7
7
 
8
- extend ActiveSupport::Concern
8
+ included do
9
+ # Delegar el acceso tipo "Enumerable":
10
+ delegate :any?, to: :@attributes
9
11
 
10
- included do
11
- # Delegar el acceso tipo "Enumerable":
12
- delegate :any?, to: :@attributes
12
+ # Re-establece el acceso tipo Hash:
13
+ delegate :[], to: :@attributes
14
+ delegate :[]=, to: :@attributes
15
+ self.site = "http://www.example.com"
13
16
 
14
- # Re-establece el acceso tipo Hash:
15
- delegate :[], to: :@attributes
16
- delegate :[]=, to: :@attributes
17
- self.site = "http://www.example.com"
18
-
19
- attribute_name = self.name.demodulize.underscore[0..-("_translations".length+1)]
20
- alias_method attribute_name.to_sym, :lookup
21
- end
17
+ attribute_name = self.name.demodulize.underscore[0..-("_translations".length+1)]
18
+ alias_method attribute_name.to_sym, :lookup
19
+ end
22
20
 
23
- def locales
24
- @attributes.keys
25
- end
26
- alias_method :available_locales, :locales
27
-
28
- def lookup
29
- locale = ::I18n.locale
30
- if ::I18n.respond_to?(:fallbacks)
31
- lookup_result = @attributes[::I18n.fallbacks[locale].map(&:to_s).find{ |loc| @attributes.has_key?(loc) }]
32
- else
33
- lookup_result = @attributes[locale.to_s]
34
- end
35
- lookup_result || @attributes.present? ? @attributes.first[1] : nil
21
+ def locales
22
+ @attributes.keys
23
+ end
24
+ alias_method :available_locales, :locales
25
+
26
+ def lookup
27
+ locale = ::I18n.locale
28
+ if ::I18n.respond_to?(:fallbacks)
29
+ lookup_result = @attributes[::I18n.fallbacks[locale].map(&:to_s).find{ |loc| @attributes.has_key?(loc) }]
30
+ else
31
+ lookup_result = @attributes[locale.to_s]
36
32
  end
37
-
33
+ lookup_result || @attributes.present? ? @attributes.first[1] : nil
38
34
  end
39
- end
40
35
 
41
- end
36
+ end
37
+ end
@@ -0,0 +1,25 @@
1
+ module NaranyaEcm::Behaviors
2
+ module Mediable
3
+
4
+ extend ActiveSupport::Concern
5
+
6
+ def find_media_by_role(role_name)
7
+ mr = self.media_resources.nil? ? [] : self.media_resources
8
+ mr.detect { |mr| mr.roles.include? role_name }
9
+ end
10
+
11
+ def find_media_url_by_role(role_name)
12
+ mr = self.find_media_by_role role_name
13
+ mr.present? ? mr.downloadable_url : nil
14
+ end
15
+
16
+ def select_media_by_role(role_name)
17
+ self.media_resources.select { |mr| mr.roles.include? role_name }
18
+ end
19
+
20
+ def select_media_urls_by_role(role_name)
21
+ mr = self.select_media_by_role(role_name).map(&:downloadable_url)
22
+ mr.flatten.uniq
23
+ end
24
+ end
25
+ end
@@ -1,22 +1,25 @@
1
1
  require 'active_support/concern'
2
- module NaranyaEcm
3
- module Behaviors
4
- module Timestampable
5
2
 
6
- extend ActiveSupport::Concern
3
+ module NaranyaEcm::Behaviors
4
+ module Timestampable
7
5
 
8
- included do
9
- %w(created_at updated_at).map(&:to_sym).each do |attr_sym|
10
- # getter:
11
- define_method(attr_sym) { @attributes[attr_sym].present? ? @attributes[attr_sym].to_datetime : nil }
12
- # setter:
13
- # define_method("#{attr_sym}=".to_sym) do |value|
14
- # self.send "#{attr_sym}_will_change!".to_sym unless value == @attributes[attr_sym]
15
- # @attributes[attr_sym] = value
16
- # end
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ [:created_at, :updated_at].each do |timestamp_name|
10
+ attr_accessor timestamp_name
11
+
12
+ define_method timestamp_name do
13
+ unless instance_variable_get("@#{timestamp_name}".to_sym).respond_to? :strftime
14
+ instance_variable_set(
15
+ "@#{timestamp_name}".to_sym,
16
+ instance_variable_get("@#{timestamp_name}".to_sym).to_s.to_datetime
17
+ )
18
+ end
19
+ instance_variable_get("@#{timestamp_name}".to_sym)
17
20
  end
18
21
  end
19
-
20
22
  end
23
+
21
24
  end
22
- end
25
+ end
@@ -1,73 +1,49 @@
1
1
  require 'active_support/hash_with_indifferent_access'
2
2
 
3
- module NaranyaEcm
4
- module Cache
5
- class Key < String
3
+ module NaranyaEcm::Cache
4
+ class Key < String
6
5
 
7
- TIMESTAMP_FORMAT = '%Y%m%d%H%M%S'.freeze
6
+ TIMESTAMP_FORMAT = '%Y%m%d%H%M%S'.freeze
8
7
 
9
- def initialize(segments={})
8
+ attr_reader :klass, :id, :timestamp
10
9
 
11
- @segments = ActiveSupport::HashWithIndifferentAccess.new
10
+ def initialize(given_class, given_id, given_timestamp = nil)
11
+ @klass, @id = given_class, given_id
12
12
 
13
- resource = segments.delete :resource
14
-
15
- @segments[:class_name] = case
16
- when resource.present? then resource.class.name
17
- when segments.has_key?(:class_name) then segments.delete(:class_name)
18
- when segments.has_key?(:resource_class) then segments.delete(:resource_class).name
19
- else nil
20
- end
21
-
22
- @segments[:id] = case
23
- when resource.present? then resource.id
24
- when segments.has_key?(:id) then segments.delete(:id)
25
- else nil
26
- end
27
-
28
- @segments[:timestamp] = case
29
- when resource.present? then resource.respond_to?(:updated_at) ? resource.updated_at : nil
30
- when segments.has_key?(:timestamp) then segments.delete(:timestamp)
31
- else nil
13
+ unless given_timestamp.blank?
14
+ @timestamp = if given_timestamp.is_a? String
15
+ given_timestamp.to_datetime
16
+ elsif given_timestamp.respond_to? :strftime
17
+ given_timestamp
18
+ else
19
+ raise ArgumentError, "Given timestamp is not a Date|Time like object"
32
20
  end
33
-
34
- @segments.freeze
35
-
36
- super self.class.join_segments(@segments).freeze
37
-
38
21
  end
39
22
 
40
- def class_name; @segments[:class_name]; end
41
- def id; @segments[:id]; end
42
- def timestamp; @segments[:timestamp]; end
23
+ super join_segments
24
+ end
43
25
 
44
- def timestamped?
45
- resource_timestamp.present?
46
- end
26
+ def timestamped?
27
+ timestamp.present?
28
+ end
47
29
 
48
- def any_timestamp
49
- self.class.join_segments(@segments.except(:timestamp)) + '-*'
50
- end
30
+ def exist?
31
+ NaranyaEcm.cache.exist? self
32
+ end
51
33
 
52
- def with_timestamp(datetime)
53
- raise "Not a valid timestamp" unless datetime.respond_to? :to_time
54
- self.class.new class_name: class_name, id: id, timestamp: datetime
55
- end
34
+ def read
35
+ NaranyaEcm.cache.read self
36
+ end
56
37
 
57
- class << self
58
- def join_segments(segments)
59
- segments = segments.with_indifferent_access
60
- str = segments[:class_name].tableize
61
- if segments[:id].present?
62
- str += "/#{segments[:id]}"
63
- if segments[:timestamp].present? and segments[:timestamp].respond_to?(:to_time)
64
- str += '-' + segments[:timestamp].strftime(TIMESTAMP_FORMAT)
65
- end
66
- end
67
- str
68
- end
38
+ def write(data)
39
+ NaranyaEcm.cache.write self, data
40
+ end
41
+
42
+ private
43
+ def join_segments
44
+ key = "#{klass.name.tableize}/#{id}"
45
+ timestamped? ? "#{key}-#{timestamp.strftime(TIMESTAMP_FORMAT)}" : key
69
46
  end
70
47
 
71
- end
72
48
  end
73
49
  end
@@ -1,82 +1,69 @@
1
1
  require 'active_support/concern'
2
- module NaranyaEcm
3
- module Cache
4
- module Methods
5
2
 
6
- extend ActiveSupport::Concern
3
+ module NaranyaEcm::Cache
4
+ module Methods
7
5
 
8
- included do
9
- alias_method_chain :load, :caching
6
+ extend ActiveSupport::Concern
10
7
 
11
- class << self
12
- alias_method_chain :find_single, :caching
13
- end
14
- end
15
-
16
- def cache_key
17
- @cache_key ||= NaranyaEcm.cache_key_for resource: self
18
- end
19
-
20
- def load_with_caching(attributes, remove_root = false, persisted = false)
21
- if self.new?
22
- load_without_caching(attributes, remove_root, persisted)
23
- else
24
- res = load_without_caching(attributes, remove_root = false, persisted = false)
25
-
26
- cache_options = {}
27
- cache_options[:expires_in] = 10.minutes unless self.updated_at.present? and self.updated_at.is_a? DateTime
28
-
29
- NaranyaEcm.cache.write cache_key, @attributes, cache_options
30
-
31
- res
32
- end
8
+ included do
9
+ class << self
10
+ alias_method_chain :find, :caching
11
+ alias_method_chain :find_one, :caching
33
12
  end
13
+ end
34
14
 
35
- module ClassMethods
15
+ def cache_key
16
+ self.new_resource? ? nil : self.class.cache_key_for(self.id, self.updated_at)
17
+ end
36
18
 
37
- def cache
38
- NaranyaEcm.cache
39
- end
19
+ module ClassMethods
40
20
 
41
- def cache_key_for(attributes)
42
- NaranyaEcm.cache_key_for attributes.merge(resource_class: self)
43
- end
21
+ def cache
22
+ NaranyaEcm.cache
23
+ end
44
24
 
45
- def find_cached(cache_key)
46
- cached_attributes = cache.read(cache_key)
47
- cached_attributes.present? ? instantiate_record(cached_attributes) : nil
48
- end
25
+ def cache_key_for(id, timestamp=nil)
26
+ Key.new self, id, timestamp
27
+ end
49
28
 
50
- def find_cached!(cache_key)
51
- found = find_cached(cache_key)
52
- raise "No record matched given attribute in cache" unless found
53
- found
54
- end
29
+ def find_with_caching(id, cache_options = {})
30
+ find_one_with_caching(id, cache_options)
31
+ end
55
32
 
56
- private
33
+ private
57
34
 
58
- # Find a single resource from the default URL
59
- def find_single_with_caching(scope, options)
60
-
35
+ # Find a single resource from the default URL
36
+ def find_one_with_caching(id, cache_options = {})
37
+ fetched_data = nil
38
+ missed = true
39
+
40
+ # Ver primero si lo podemos obtener del cache:
41
+ cache_timestamp = cache_options.delete(:timestamp)
42
+ if cache_timestamp.present?
61
43
  # Try to load the record from cache:
62
- cache_key = cache_key_for id: scope, timestamp: options.delete(:timestamp)
63
-
64
- record = find_cached cache_key
65
-
66
- unless record.present?
44
+ cache_key = cache_key_for(id, cache_timestamp)
45
+ missed = (fetched_data = cache_key.read).nil?
46
+ puts "===== CACHE #{(missed ? 'MISS: ' : 'HIT: ')} '#{cache_key}'"
47
+ end
67
48
 
68
- # Load the record from the ECM server:
69
- record = find_single_without_caching(scope, options)
49
+ # Si no lo obtuvimos del caché, traer uno del server:
70
50
 
71
- # Cache the record by writing the record's attributes to the cache store,
72
- # IF the attributes have not been written before:
73
- cache.write(record.cache_key, record.attributes) unless cache.exist? record.cache_key
74
- end
75
-
76
- record
51
+ unless fetched_data.present?
52
+ fetched_data = fetch_one(id)
53
+ end
54
+
55
+ # Inicializar el recurso con los datos obtenidos:
56
+ resource = self.load fetched_data
57
+
58
+ # Write unless data exists:
59
+ if missed
60
+ puts "===== Cache WRITE: '#{resource.cache_key}'"
61
+ cache.write(resource.cache_key, fetched_data)
77
62
  end
78
- end
79
63
 
64
+ resource
65
+ end
80
66
  end
67
+
81
68
  end
82
- end
69
+ end
@@ -2,35 +2,58 @@ require 'active_support/concern'
2
2
  require 'state_machine'
3
3
 
4
4
  module NaranyaEcm
5
- module Lifecycles
6
5
 
7
- module ContentLifecycle
8
- extend ActiveSupport::Concern
6
+ module ContentLifecycle
7
+ extend ActiveSupport::Concern
9
8
 
10
- included do
9
+ included do
11
10
 
12
- include NaranyaEcm::Lifecycles::Lifecycleable
11
+ state_machine :lifecycle_state, initial: :draft do
12
+ state :draft
13
+ state :awaiting_validation
14
+ state :accepted
15
+ state :rejected
16
+ state :published
17
+ state :inactive
13
18
 
14
- ##################################################################
15
- # StateMachine:
16
- state_machine :lifecycle_state, initial: :draft do
19
+ event :send_to_validation do
20
+ transition :draft => :awaiting_validation
21
+ end
17
22
 
18
- state :draft
23
+ event :accept do
24
+ transition :awaiting_validation => :accepted
25
+ end
19
26
 
20
- state :awaiting_validation
27
+ event :reject do
28
+ transition [:awaiting_validation, :published] => :rejected
29
+ end
21
30
 
22
- state :rejected
31
+ event :publish do
32
+ transition :draft => :published, if: :simple_lifecycle?
33
+ transition :accepted => :published, if: :validation_lifecycle?
34
+ end
23
35
 
24
- state :validated
36
+ event :deactivate do
37
+ transition any => :inactive
38
+ end
25
39
 
26
- state :published
40
+ ######
41
+ after_transition :on => any, do: :commit_lifecycle_state_change!
42
+ end
27
43
 
28
- state :deactivated
44
+
45
+ end
29
46
 
30
- end
31
- end
47
+ def simple_lifecycle?; self.lifecycle_name == 'simple'; end
48
+ def validation_lifecycle?; self.lifecycle_name == 'validation'; end
32
49
 
50
+ def commit_lifecycle_state_change!(transition)
51
+ off_band_changes["#{transition.attribute}_event"] = transition.event
52
+ self.send "reset_#{transition.attribute}!".to_sym
53
+ self.save
33
54
  end
34
55
 
56
+
35
57
  end
58
+
36
59
  end
@@ -1,40 +1,21 @@
1
- require 'active_resource'
2
1
 
3
- module NaranyaEcm::Models
4
- class Category < ::ActiveResource::Base
5
-
6
- include NaranyaEcm::Behaviors::Resourceable
2
+ module NaranyaEcm
3
+ class Category
7
4
 
5
+ include NaranyaEcm.document_module
8
6
  include NaranyaEcm::Behaviors::Timestampable
9
-
10
- include NaranyaEcm::Search::Methods
11
7
  include NaranyaEcm::Cache::Methods
8
+ include NaranyaEcm::Search::Methods
9
+ include NaranyaEcm::Behaviors::Mediable
12
10
 
13
- class NameTranslations < ::ActiveResource::Base
14
- include NaranyaEcm::Behaviors::Localizable
15
- end
16
-
17
- self.include_root_in_json = true
18
-
19
- # Definir los atributos conocidos:
20
- schema do
21
- # define each attribute separately
22
- string :id, :code, :created_at, :updated_at
23
-
24
- # unsupported types should be left as strings
25
- # overload the accessor methods if you need to convert them
26
- #attribute 'created_at', 'string'
27
- attribute 'name_translations', 'string'
28
- attribute 'description_translations', 'string'
29
-
30
- end
11
+ field :code, type: String
12
+ validates :code, presence: true
31
13
 
32
- delegate :name, to: :name_translations
33
- delegate :description, to: :title_translations
14
+ field :name, type: String, localize: true
15
+
16
+ has_many :media_resources
34
17
 
35
- def contents
36
- Content.find :all, params: { category_id: self.id }
37
- end
18
+ has_many :contents
38
19
 
39
20
  end
40
21
  end
@@ -1,84 +1,84 @@
1
- require 'active_resource'
2
1
 
3
- module NaranyaEcm::Models
4
- class Content < ::ActiveResource::Base
2
+ module NaranyaEcm
3
+ class Content
5
4
 
6
- include ActiveModel::Dirty
7
-
8
- include NaranyaEcm::Behaviors::Resourceable
9
-
10
- include NaranyaEcm::Search::Methods
5
+ include NaranyaEcm.document_module
6
+ include NaranyaEcm::Behaviors::Timestampable
11
7
  include NaranyaEcm::Cache::Methods
8
+ include NaranyaEcm::Search::Methods
9
+ include NaranyaEcm::Behaviors::Mediable
10
+
11
+ # Type:
12
+ # Describes the main type of the content - App, Note, etc.
13
+ field :type, type: String
14
+
15
+ # Content Lifecycle Name:
16
+ # Determines the lifecycle for the specific content:
17
+ # - simple: draft -> published -> inactive
18
+ # - validatable: draft -> awaiting_validation -> validated -> published -> inactive
19
+ # - expirable: draft -> published -> expired/inactive
20
+ field :lifecycle_name, type: String, default: -> { 'simple' }
21
+ validates :lifecycle_name, presence: true
22
+
23
+ # Content Lifecycle State:
24
+ # A managed string that describes the current state of this particular content.
25
+ # Depending on the Content's lifecycle, it changes upon several events.
26
+ # - draft:
27
+ # - awaiting_validation:
28
+ # - validated:
29
+ # - invalidated:
30
+ # - published:
31
+ field :lifecycle_state, type: String, default: -> { :draft }
32
+ validates :lifecycle_state, presence: true
33
+
34
+ # Title (Localized):
35
+ # The content title that can be displayed as header, result, etc.
36
+ field :title, type: String, localize: true
37
+ validate :at_least_one_title_translation_must_exist
38
+
39
+ # Description (Localized):
40
+ # The content title that can be displayed as header, result, etc.
41
+ field :description, type: String, localize: true
12
42
 
13
- class TitleTranslations < EmbeddedLocalizedHash; end
14
- class DescriptionTranslations < EmbeddedLocalizedHash; end
15
- class ContentRating < EmbeddedHash; end
16
-
17
- self.include_root_in_json = true
18
-
19
- has_many :versions, class_name: "naranya_ecm/models/content_version"
20
- belongs_to :current_version, class_name: "naranya_ecm/models/content_version"
21
-
22
- belongs_to :category, class_name: "naranya_ecm/models/category"
23
-
24
- has_many :media_resources, class_name: "naranya_ecm/models/media_resource"
25
-
26
- # Definir los atributos conocidos:
27
- schema do
28
- # define each attribute separately
29
- string :id, :type, :lifecycle_name, :lifecycle_state, :content_rating, :created_at, :updated_at
30
- string :category_id
31
- string :author
32
- string :main_url
33
-
34
- # unsupported types should be left as strings
35
- # overload the accessor methods if you need to convert them
36
- #attribute 'created_at', 'string'
37
- attribute 'title_translations', 'string'
38
- attribute 'description_translations', 'string'
39
- attribute 'keywords', 'string'
40
-
41
- if NaranyaEcm.options[:extra_attributes] && NaranyaEcm.options[:extra_attributes][:content]
42
- NaranyaEcm.options[:extra_attributes][:content].each { |key, val| attribute key, val }
43
- end
44
-
45
- end
46
-
47
- schema_attrs = self.schema.keys.map(&:to_sym)
48
- define_attribute_methods(*schema_attrs)
49
- schema_attrs.each do |attr_sym|
50
- # getter:
51
- define_method(attr_sym) { @attributes[attr_sym] }
52
- # setter:
53
- define_method("#{attr_sym}=".to_sym) do |value|
54
- self.send "#{attr_sym}_will_change!".to_sym unless value == @attributes[attr_sym]
55
- @attributes[attr_sym] = value
56
- end
57
- end
58
-
59
- delegate :title, to: :title_translations
60
- delegate :description, to: :description_translations
61
-
62
- def initialize(attributes = {}, persisted = false)
63
- super
64
- attributes[:title_translations] = TitleTranslations.new unless attributes[:title_translations].present?
65
- attributes[:description_translations] = DescriptionTranslations.new unless attributes[:description_translations].present?
66
- attributes[:content_rating] = ContentRating.new unless attributes[:content_rating].present?
67
- end
43
+ # Content Rating:
44
+ field :content_rating, type: Hash
45
+
46
+ # Keywords:
47
+ field :keywords, type: Array
48
+
49
+ # Author:
50
+ # The name of the user who created this content.
51
+ field :author, type: String
52
+ validates :author,
53
+ presence: true
54
+
55
+ # Main URL:
56
+ # The URL that should be used to render or display the content.
57
+ field :main_url, type: String
58
+ validates :main_url,
59
+ format: { with: URI.regexp },
60
+ allow_nil: true,
61
+ allow_blank: true
62
+
63
+ belongs_to :category
64
+
65
+ has_many :versions,
66
+ class_name: :ContentVersion
68
67
 
69
- include NaranyaEcm::Behaviors::Timestampable
68
+ belongs_to :current_version,
69
+ class_name: :ContentVersion
70
70
 
71
- before_save :keywords_array!
71
+ has_many :media_resources,
72
+ class_name: :MediaResource,
73
+ inverse_of: :content
72
74
 
73
- def keywords_array!
74
- unless self.attributes[:keywords].is_a?(Array)
75
- self.attributes[:keywords] = if self.attributes[:keywords]=='' then []
76
- else [self.attributes[:keywords]]
77
- end
78
- end
79
- end
75
+ include NaranyaEcm::ContentLifecycle
80
76
 
81
- #include NaranyaEcm::Lifecycles::ContentLifecycle
77
+ private
78
+
79
+ def at_least_one_title_translation_must_exist
80
+ errors.add :title_translations, "must have at least one translation" unless title_translations.keys.any?
81
+ end
82
82
 
83
83
  end
84
84
  end