naranya_ecm-sdk 0.0.14 → 0.0.15

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -1,36 +1,57 @@
1
- require 'active_resource'
2
1
 
3
- module NaranyaEcm::Models
4
- class ContentVersion < ::ActiveResource::Base
2
+ module NaranyaEcm
3
+ class ContentVersion
5
4
 
6
- class DescriptionTranslations < EmbeddedLocalizedHash; end
7
-
8
- include NaranyaEcm::Behaviors::Resourceable
5
+ include NaranyaEcm.document_module
9
6
  include NaranyaEcm::Behaviors::Timestampable
7
+ include NaranyaEcm::Cache::Methods
8
+ include NaranyaEcm::Behaviors::Mediable
9
+
10
+ # Description (localized):
11
+ # A localized string that describes an overview of the significant changes
12
+ # particular in this version. Used to list the most significant updates, fixes, etc.
13
+ # to the end user.
14
+ field :description, type: String, localize: true
15
+
16
+ # tomar lifecycle de content:
17
+ attr_accessor :lifecycle_name
18
+ #field :lifecycle_name, type: String
19
+
20
+ # Version Lifecycle State:
21
+ # A managed string that describes the current state of this particular version.
22
+ # Depending on the Content's lifecycle, it changes upon several events.
23
+ # - draft:
24
+ # - awaiting_validation:
25
+ # - validated:
26
+ # - invalidated:
27
+ # - published:
28
+ # - superceded:
29
+ field :lifecycle_state, type: String, default: -> { :draft }
30
+ validates :lifecycle_state, presence: true
31
+
32
+ # Version Code:
33
+ # An internal version number. This number is used only to determine whether one version is more recent than another, with higher numbers indicating more recent versions. This is not the version number shown to users; that number is set by the versionName attribute.
34
+ # The value must be set as an integer, such as "100". You can define it however you want, as long as each successive version has a higher number. For example, it could be a build number. Or you could translate a version number in "x.y" format to an integer by encoding the "x" and "y" separately in the lower and upper 16 bits. Or you could simply increase the number by one each time a new version is released.
35
+ field :code, type: Integer
36
+ validates :code, presence: true, format: { with: /[0-9]+/ }
37
+
38
+ # Version Name:
39
+ # The version number shown to users.
40
+ # The string has no other purpose than to be displayed to users. The Version Code
41
+ # attribute holds the significant version number used internally.
42
+ field :name, type: String
43
+
44
+ # Content
45
+ # The content to which this particular version belongs:
46
+ belongs_to :content, class_name: :Content, inverse_of: :versions
10
47
 
11
- self.include_root_in_json = true
12
-
13
- # Definir los atributos conocidos:
14
- schema do
15
- # define each attribute separately
16
- string :id, :name, :lifecycle_name, :lifecycle_state
17
- integer :code
18
- end
19
-
20
- delegate :description, to: :description_translations
21
-
22
- belongs_to :content, class_name: "naranya_ecm/models/content"
23
- has_many :media_resources, class_name: "naranya_ecm/models/media_resource"
24
-
25
- before_save :filter_out_read_only_attributes
26
- def filter_out_read_only_attributes
27
- %w(lifecycle_name content media_resources url created_at updated_at).each do |roa|
28
- self.attributes.delete roa
29
- end
30
- end
31
-
32
- #include NaranyaEcm::Lifecycles::Lifecycleable
33
- # include NaranyaEcm::Lifecycles::VersionLifecycle
48
+ # MediaResources:
49
+ # A list of media - images, audio, video, etc. associated
50
+ # directly to this particular content version. Some examples:
51
+ # - Icons, Banners, screenshots, gallery images
52
+ has_many :media_resources, class_name: :MediaResource, inverse_of: :content_version
34
53
 
54
+ include NaranyaEcm::ContentLifecycle
55
+
35
56
  end
36
57
  end
@@ -1,41 +1,37 @@
1
- require 'active_resource'
2
1
 
3
- module NaranyaEcm::Models
4
- class DownloadAuthorization < ActiveResource::Base
2
+ module NaranyaEcm
3
+ class DownloadAuthorization
5
4
 
6
- include NaranyaEcm::Cache::Methods
7
-
8
- class Tokens < HashWithIndifferentAccess
9
- def initialize(*args)
10
- super()
11
- given_attrs = args.shift
12
- given_attrs.each { |k,v| self[k] = v } unless given_attrs.nil?
13
- end
14
- def to_query_params
15
- self.map { |key, value| "#{key}=#{value}" }.join '&'
16
- end
17
- end
18
-
19
- include NaranyaEcm::Behaviors::Resourceable
5
+ include NaranyaEcm.document_module
20
6
  include NaranyaEcm::Behaviors::Timestampable
7
+ include NaranyaEcm::Cache::Methods
21
8
 
22
- self.include_root_in_json = true
23
-
24
- # Definir los atributos conocidos:
25
- schema do
26
- string :id, :media_resource_id, :expires_at, :tokens, :created_at, :updated_at
27
- end
28
-
29
- belongs_to :media_resource, class_name: "naranya_ecm/models/media_resource"
9
+ belongs_to :media_resource
10
+ # validates :media_resource, presence: true
11
+
12
+ field :expires_at, type: DateTime, default: -> { 5.minutes.from_now }
13
+ field :tokens, type: Hash
30
14
 
31
15
  def authorized_url
32
16
  unless @authorized_url
33
17
  uri = URI(self.media_resource.downloadable_url)
34
- uri.query = self.tokens.to_query_params
18
+ uri.query = self.tokens.map { |key, value| "#{key}=#{value}" }.join '&'
35
19
  @authorized_url = uri.to_s
36
20
  end
37
21
  @authorized_url
38
22
  end
39
23
 
24
+ # validate :requested_media_resource_is_private
25
+ # def requested_media_resource_is_private
26
+ # errors.add :media_resource, "must be private" unless self.media_resource.private?
27
+ # end
28
+
29
+ # validate :requested_media_resource_is_valid
30
+ # def requested_media_resource_is_valid
31
+ # unless self.media_resource.valid?
32
+ # errors.add :media_resource, self.media_resource.errors.messages
33
+ # end
34
+ # end
35
+
40
36
  end
41
37
  end
@@ -1,28 +1,62 @@
1
- require 'active_resource'
2
1
 
3
- module NaranyaEcm::Models
4
- class MediaResource < ActiveResource::Base
2
+ module NaranyaEcm
3
+ class MediaResource
5
4
 
6
- include NaranyaEcm::Behaviors::Resourceable
5
+ include NaranyaEcm.document_module
7
6
  include NaranyaEcm::Behaviors::Timestampable
7
+ include NaranyaEcm::Cache::Methods
8
8
 
9
- class CompatibleDeviceOses < EmbeddedHash; end
9
+ # Media MIME/Internet Type:
10
+ field :type, type: String
10
11
 
11
- include NaranyaEcm::Cache::Methods
12
+ # The roles of the media file (screenshot, icon, main_downloadable):
13
+ field :roles, type: Array, default: -> {[]}
14
+
15
+ # The URL to the downloadable file:
16
+ field :downloadable_url, type: String
17
+ validates :downloadable_url, presence: true, format: { with: URI.regexp }
12
18
 
13
- self.include_root_in_json = true
19
+ # The access type of the media file (private|public)
20
+ field :access_type, type: String, default: -> { 'public' }
21
+ validates :access_type, presence: true
22
+
23
+ # The list of compatible device ids:
24
+ field :compatible_device_ids, type: Array, default: -> { [] }
14
25
 
15
- # Definir los atributos conocidos:
16
- schema do
17
- # define each attribute separately
18
- string :id, :access_type, :type, :roles, :downloadable_url
26
+ # The list of compatible device operating systems:
27
+ field :compatible_device_oses, type: Hash, default: -> { {} }
19
28
 
20
- # unsupported types should be left as strings
21
- # overload the accessor methods if you need to convert them
22
- attribute 'compatible_device_ids', 'string'
23
- attribute 'compatible_device_oses', 'string'
29
+ belongs_to :content
30
+ belongs_to :content_version
31
+ belongs_to :category
24
32
 
33
+ def private?
34
+ self.access_type == 'private'
25
35
  end
26
36
 
37
+ # Media Resource State:
38
+ # A managed string that describes the current state of this particular media.
39
+ # - unlinked: The media resource is linked to a category, content or content_version.
40
+ # - linked: The media resource is detached from any associated document.
41
+ field :state, type: String, default: -> { :unlinked }
42
+ validates :state, presence: true
43
+ state_machine :state, initial: :unlinked do
44
+
45
+ state :linked
46
+ state :unlinked
47
+
48
+ event :unlink do
49
+ transition :linked => :unlinked
50
+ end
51
+
52
+ after_transition :on => any, do: :commit_state_change!
53
+ end
54
+
55
+ def commit_state_change!(transition)
56
+ off_band_changes["#{transition.attribute}_event"] = transition.event
57
+ self.send "reset_#{transition.attribute}!".to_sym
58
+ self.save
59
+ end
60
+
27
61
  end
28
62
  end
@@ -0,0 +1,156 @@
1
+ module NaranyaEcm::Rest
2
+
3
+ module Associations
4
+ extend ActiveSupport::Concern
5
+
6
+ class Association
7
+ attr_reader :klass, :name, :associated_class, :options
8
+
9
+ def resolve_class_name(given_class_name)
10
+ given_class_name = "#{@klass.name.deconstantize}::#{given_class_name}" if given_class_name.is_a?(Symbol)
11
+ given_class_name.constantize
12
+ end
13
+
14
+ def initialize(klass, name, options = {})
15
+ @klass, @name = klass, name
16
+
17
+ @associated_class = if (given_class_name = options.delete(:class_name))
18
+ resolve_class_name(given_class_name)
19
+ else
20
+ resolve_class_name(name.to_s.classify.to_sym)
21
+ end
22
+
23
+ @options = options.with_indifferent_access
24
+
25
+ klass.send :attr_accessor, name
26
+
27
+ end
28
+ end
29
+
30
+ class CollectionAssociation < Association
31
+ end
32
+
33
+ class SingularAssociation < Association
34
+ end
35
+
36
+ class HasMany < Association
37
+ def foreign_key
38
+ @foreign_key ||= @options[:foreign_key] || @options.has_key?(:inverse_of) ? "#{@options[:inverse_of]}_id".to_sym : "#{@klass.name.demodulize.underscore}_id".to_sym
39
+ end
40
+ def initialize(klass, name, options = {})
41
+ super
42
+ # Define the Getter:
43
+ klass.send :define_method, name do
44
+ value = self.instance_variable_get "@#{name}".to_sym
45
+ if value.nil?
46
+ reflection = self.class.reflect_on_association name
47
+ foreign_key = (self.class.name.demodulize.underscore + '_id').to_sym
48
+ value = self.instance_variable_set(
49
+ "@#{name}".to_sym,
50
+ reflection.associated_class.where(foreign_key => self.id)
51
+ )
52
+ end
53
+ value
54
+ end
55
+
56
+ # Define the setter
57
+ klass.send :define_method, "#{name}=".to_sym do |new_value|
58
+ reflection = self.class.reflect_on_association(name)
59
+ relation = Relation.new(reflection.associated_class, { reflection.foreign_key => self.id })
60
+
61
+ unless new_value.blank?
62
+ relation.load new_value
63
+ end
64
+
65
+ instance_variable_set "@#{name}".to_sym, relation
66
+ end
67
+ end
68
+ def macro; :has_many; end
69
+ end
70
+
71
+ class HasOne < SingularAssociation
72
+ def initialize(klass, name, options = {})
73
+ super
74
+ # Set the Getter:
75
+ klass.send :define_method, name do
76
+ value = self.instance_variable_get "@#{name}".to_sym
77
+ if value.nil?
78
+ reflection = self.class.reflect_on_association name
79
+ foreign_key = (self.class.name.demodulize.underscore + '_id').to_sym
80
+ value = self.instance_variable_set(
81
+ "@#{name}".to_sym,
82
+ reflection.associated_class.where(foreign_key => self.id).first
83
+ )
84
+ end
85
+ value
86
+ end
87
+ end
88
+ def macro; :has_one; end
89
+ end
90
+
91
+ class BelongsTo < SingularAssociation
92
+ def initialize(klass, name, options = {})
93
+ super
94
+
95
+ # Define the foreign key:
96
+ klass.send :field, foreign_key, foreign_key: true
97
+
98
+ # Define the setter
99
+ klass.send :define_method, "#{name}=".to_sym do |new_value|
100
+ reflection = self.class.reflect_on_association(name)
101
+ foreign_key_name = reflection.foreign_key
102
+
103
+ new_value = reflection.associated_class.load new_value if new_value.is_a? Hash
104
+ raise "Type Mismatch on #{self.class.name} ##{name}= #{new_value.inspect}" unless new_value.nil? || new_value.class == reflection.associated_class
105
+
106
+ self.send "#{foreign_key_name}=".to_sym, new_value.nil? ? nil : new_value.id
107
+ instance_variable_set "@#{name}".to_sym, new_value
108
+ end
109
+ end
110
+ def macro; :belongs_to; end
111
+ def foreign_key
112
+ @foreign_key ||= options[:foreign_key] || "#{name}_id".to_sym
113
+ end
114
+ end
115
+
116
+ def is_association?(association_name)
117
+ self.class.is_association? association_name
118
+ end
119
+
120
+ module ClassMethods
121
+
122
+ def is_association?(association_name)
123
+ associations.has_key? association_name
124
+ end
125
+
126
+ def has_many(name, options={})
127
+ associations[name] = HasMany.new self, name, options
128
+ end
129
+
130
+ def has_one(name, options={})
131
+ associations[name] = HasOne.new self, name, options
132
+ end
133
+
134
+ def belongs_to(name, options={})
135
+ associations[name] = BelongsTo.new self, name, options
136
+ end
137
+
138
+ def reflect_on_association(association_name)
139
+ raise "Association #{name} does not exist" unless is_association? association_name
140
+ associations[association_name]
141
+ end
142
+
143
+ def reflect_on_all_associations
144
+ associations
145
+ end
146
+
147
+ private
148
+
149
+ def associations
150
+ @associations ||= {}.with_indifferent_access
151
+ end
152
+
153
+ end
154
+ end
155
+
156
+ end
@@ -0,0 +1,4 @@
1
+ module NaranyaEcm::Rest
2
+ class Client
3
+ end
4
+ end
@@ -0,0 +1,53 @@
1
+ module NaranyaEcm::Rest
2
+ # = REST Errors
3
+ #
4
+ # Generic REST exception class.
5
+ class RestError < StandardError
6
+ end
7
+
8
+ # Raised when an object assigned to an association has an incorrect type.
9
+ #
10
+ # class Ticket < ActiveRecord::Base
11
+ # has_many :patches
12
+ # end
13
+ #
14
+ # class Patch < ActiveRecord::Base
15
+ # belongs_to :ticket
16
+ # end
17
+ #
18
+ # # Comments are not patches, this assignment raises AssociationTypeMismatch.
19
+ # @ticket.patches << Comment.new(content: "Please attach tests to your patch.")
20
+ class AssociationTypeMismatch < RestError
21
+ end
22
+
23
+ # Raised when unserialized object's type mismatches one specified for serializable field.
24
+ class SerializationTypeMismatch < RestError
25
+ end
26
+
27
+ # Raised when adapter not specified on connection (or configuration file <tt>config/database.yml</tt>
28
+ # misses adapter field).
29
+ class AdapterNotSpecified < RestError
30
+ end
31
+
32
+ # Raised when REST cannot find database adapter specified in <tt>config/database.yml</tt> or programmatically.
33
+ class AdapterNotFound < RestError
34
+ end
35
+
36
+ # Raised when connection to the database could not been established (for example when <tt>connection=</tt>
37
+ # is given a nil object).
38
+ class ConnectionNotEstablished < RestError
39
+ end
40
+
41
+ # Raised when REST cannot find record by given id or set of ids.
42
+ class ResourceNotFound < RestError
43
+ end
44
+
45
+ # Raised by ActiveRecord::Base.save! and ActiveRecord::Base.create! methods when record cannot be
46
+ # saved because record is invalid.
47
+ class ResourceNotSaved < RestError
48
+ end
49
+
50
+ # Raised by ActiveRecord::Base.destroy! when a call to destroy would return false.
51
+ class ResourceNotDestroyed < RestError
52
+ end
53
+ end
@@ -0,0 +1,50 @@
1
+ require 'naranya_ecm/rest/errors'
2
+ module NaranyaEcm::Rest
3
+
4
+ module FinderMethods
5
+
6
+ def find(*args)
7
+ if block_given?
8
+ to_a.find { |*block_args| yield(*block_args) }
9
+ else
10
+ find_with_ids(*args)
11
+ end
12
+ end
13
+
14
+ protected
15
+
16
+ def find_with_ids(*ids)
17
+ expects_array = ids.first.kind_of?(Array)
18
+ return ids.first if expects_array && ids.first.empty?
19
+
20
+ ids = ids.flatten.compact.uniq
21
+
22
+ case ids.size
23
+ when 0
24
+ raise ResourceNotFound, "Couldn't find #{self.name} without an ID"
25
+ when 1
26
+ result = find_one(ids.first)
27
+ expects_array ? [ result ] : result
28
+ else
29
+ find_some(ids)
30
+ end
31
+ end
32
+
33
+ def find_one(id)
34
+ self.load fetch_one(id)
35
+ end
36
+
37
+ def fetch_one(id)
38
+ format = NaranyaEcm.options[:format] || 'json'
39
+
40
+ response = self.get("#{path}/#{id}.#{format}")
41
+
42
+ case response.code
43
+ when 404
44
+ raise ResourceNotFound
45
+ when 200
46
+ response.to_hash
47
+ end
48
+ end
49
+ end
50
+ end