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
@@ -0,0 +1,215 @@
1
+ require 'httparty'
2
+ require 'active_model'
3
+
4
+ module NaranyaEcm::Rest
5
+ module Model
6
+
7
+ # Constant proc that validates if a given value is a boolean:
8
+ BOOLEAN = Proc.new { |value| !!value == value }
9
+
10
+ class LocalizedAttribute < HashWithIndifferentAccess
11
+
12
+ alias_method :locales, :keys
13
+ alias_method :available_locales, :locales
14
+
15
+ def initialize(*args)
16
+ super()
17
+ given_attrs = args.shift
18
+ given_attrs.each { |k,v| self[k] = v } unless given_attrs.nil?
19
+ end
20
+
21
+ def lookup
22
+ locale = ::I18n.locale
23
+ if ::I18n.respond_to?(:fallbacks)
24
+ lookup_result = self[::I18n.fallbacks[locale].map(&:to_s).find{ |loc| self.has_key?(loc) }]
25
+ else
26
+ lookup_result = self[locale.to_s]
27
+ end
28
+ lookup_result || self.present? ? self.first[1] : nil
29
+ end
30
+
31
+ def method_missing(method_name, *args, &block)
32
+ if method_name == self.class.aliased_lookup_name
33
+ # El nombre del método debe ser un alias de lookup.
34
+ # Agregar el alias:
35
+ self.class.send :alias_method, self.class.aliased_lookup_name, :lookup
36
+ # llamar el método de lookup. La siguiente ocación no caerá aqui:
37
+ lookup
38
+ else
39
+ super # You *must* call super if you don't handle the
40
+ # method, otherwise you'll mess up Ruby's method
41
+ # lookup.
42
+ end
43
+ end
44
+
45
+ class << self
46
+ def aliased_lookup_name
47
+ @lookup_alias ||= self.name.demodulize[0..-("Translations".length+1)].underscore.to_sym
48
+ end
49
+ end
50
+ end
51
+
52
+ extend ActiveSupport::Concern
53
+
54
+ included do
55
+
56
+ include ActiveModel::Model
57
+ extend ActiveModel::Callbacks
58
+
59
+ include ActiveModel::Dirty
60
+ primary_key :id, type: String
61
+
62
+ # Ignore the server resource's url attribute:
63
+ attr_accessor :url
64
+
65
+ include HTTParty
66
+ base_uri NaranyaEcm.options[:site]
67
+ #debug_output $stderr
68
+
69
+ include NaranyaEcm::Rest::Associations
70
+ include NaranyaEcm::Rest::Persistence
71
+
72
+ extend NaranyaEcm::Rest::FinderMethods
73
+
74
+ alias_method_chain :initialize, :defaults
75
+
76
+ if NaranyaEcm.options[:extra_attributes] && (extra_fields = NaranyaEcm.options[:extra_attributes][self.name.demodulize.underscore])
77
+ extra_fields.each do |name, type|
78
+
79
+ field_options = {}
80
+
81
+ field_options[:type] = case type
82
+ when 'boolean'
83
+ NaranyaEcm::Rest::Model::BOOLEAN
84
+ else
85
+ type.classify.constantize
86
+ end
87
+
88
+ field(name.to_sym, field_options)
89
+ end
90
+ end
91
+
92
+ end
93
+
94
+ def initialize_with_defaults(*args)
95
+ initialize_without_defaults(*args)
96
+ # inicializar con los valores default los campos que esten nulos y tengan un default definido:
97
+ self.class.fields.values
98
+ .select { |f| instance_variable_get("@#{f[:name]}".to_sym).nil? && f[:default] }
99
+ .each { |f| instance_variable_set("@#{f[:name]}".to_sym, f[:default].call) }
100
+
101
+ end
102
+
103
+ def method_missing(method, *args, &block)
104
+ if method =~ /^(\w+)=$/i
105
+ self.class.send :field, $1.to_sym, type: args.first.class
106
+ self.send method, *args
107
+ else
108
+ super # You *must* call super if you don't handle the
109
+ # method, otherwise you'll mess up Ruby's method
110
+ # lookup.
111
+ end
112
+ end
113
+
114
+ def attributes
115
+ HashWithIndifferentAccess[
116
+ self.class.fields.values
117
+ .map { |attr_data| [attr_data[:name], self.send(attr_data[:name].to_sym)] }
118
+ ]
119
+ end
120
+
121
+ def associations
122
+ self.instance_variables
123
+ .map { |instance_variable| instance_variable[1..-1].to_sym }
124
+ .select { |accessor_name| self.is_association?(accessor_name) }
125
+ .inject({}.with_indifferent_access) { |hash, accessor_name| hash[accessor_name] = self.send accessor_name; hash }
126
+ end
127
+
128
+ def load(params={})
129
+ params.each do |attr, value|
130
+ self.public_send("#{attr}=", value)
131
+ end if params
132
+ end
133
+
134
+ def path
135
+ "#{self.class.path}/#{self.id}"
136
+ end
137
+
138
+ protected
139
+
140
+ def off_band_changes
141
+ @off_band_changes ||= {}.with_indifferent_access
142
+ end
143
+
144
+
145
+ module ClassMethods
146
+
147
+ def fields
148
+ @_fields ||= {}.with_indifferent_access
149
+ end
150
+
151
+ def load(*args)
152
+ loaded = self.new
153
+ loaded.load(*args)
154
+ loaded.changed_attributes.clear
155
+ loaded
156
+ end
157
+
158
+ def primary_key(name, options = {})
159
+ attr_accessor name
160
+ define_attribute_method name
161
+ end
162
+
163
+ def field(name, options = {})
164
+
165
+ if options[:localize]
166
+ lookup_alias_name = name
167
+ name = "#{name}_translations".to_sym
168
+ end
169
+
170
+ fields[name] = {name: name}.merge(options).with_indifferent_access
171
+
172
+ attr_accessor name
173
+ define_attribute_method name
174
+
175
+ if options[:localize]
176
+ # Define localized setter with dirty notification:
177
+ define_method("#{name}=".to_sym) do |value|
178
+ value = LocalizedAttribute.new value if value.is_a?(Hash) && !value.is_a?(LocalizedAttribute)
179
+ raise "Type Mismatch..." unless value.is_a?(LocalizedAttribute)
180
+
181
+ variable_name = "@#{name}".to_sym
182
+ self.send "#{name}_will_change!".to_sym unless value == instance_variable_get(variable_name)
183
+ instance_variable_set variable_name, value
184
+ end
185
+
186
+ # Define lookup alias:
187
+ define_method lookup_alias_name do
188
+ self.send(name).lookup
189
+ end
190
+
191
+ else
192
+ # Define normal setter with dirty notification:
193
+ define_method("#{name}=".to_sym) do |value|
194
+ variable_name = "@#{name}".to_sym
195
+ self.send "#{name}_will_change!".to_sym unless value == instance_variable_get(variable_name)
196
+ instance_variable_set variable_name, value
197
+ end
198
+ end
199
+ end
200
+
201
+ def path
202
+ @path ||= "/#{self.name.demodulize.tableize}"
203
+ end
204
+
205
+ def where(given_conditions={})
206
+ Relation.new self, given_conditions.with_indifferent_access
207
+ end
208
+
209
+ def all
210
+ where()
211
+ end
212
+
213
+ end
214
+ end
215
+ end
@@ -0,0 +1,122 @@
1
+ module NaranyaEcm::Rest
2
+
3
+ module Persistence
4
+
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ define_model_callbacks :create, :update, :save
9
+ end
10
+
11
+ def save
12
+ run_callbacks :save do
13
+ @previously_changed = changes
14
+ result = self.new_resource? ? self.create : self.update
15
+ @changed_attributes.clear
16
+ result
17
+ end
18
+ end
19
+
20
+ def new_resource?; self.id.nil?; end
21
+ alias_method :new?, :new_resource?
22
+
23
+ def persisted?; !new_resource? || !self.changed?; end
24
+
25
+ protected
26
+
27
+ def load_server_errors(server_error_hash)
28
+ server_error_hash = server_error_hash.with_indifferent_access
29
+ if server_error_hash.present? && server_error_hash.has_key?(:errors) && server_error_hash[:errors].present?
30
+ server_error_hash[:errors].each do |field_name, error_list|
31
+ error_list.each do |message|
32
+ self.errors.add field_name.to_sym, message
33
+ end
34
+ end
35
+ else
36
+ self.errors.add :base, 'is invalid in server'
37
+ end
38
+ end
39
+
40
+ def reload
41
+ format = NaranyaEcm.options[:format] || 'json'
42
+ get_path = "#{path}.#{format}"
43
+ response = self.class.get get_path
44
+ case response.code
45
+ when 200
46
+ load(response.to_hash)
47
+ true
48
+ else
49
+ false
50
+ end
51
+ end
52
+
53
+ def reload!; raise "Ja!" unless reload; end
54
+
55
+ def persist_request_options(data)
56
+ {
57
+ body: ActiveSupport::JSON.encode({ self.class.name.demodulize.underscore => data }),
58
+ headers: { 'Content-Type' => 'application/json'}
59
+ }
60
+ end
61
+
62
+ def create
63
+ run_callbacks :create do
64
+
65
+ format = NaranyaEcm.options[:format] || 'json'
66
+ create_path = "#{self.class.path}.#{format}"
67
+
68
+ response = self.class.post create_path, persist_request_options(self.attributes)
69
+
70
+ case response.code
71
+ when 201
72
+ self.load(response.to_hash)
73
+ true
74
+ when 422 # Unprocessable Entity
75
+ self.load_server_errors(response.to_hash)
76
+ false
77
+ end
78
+ end
79
+ end
80
+
81
+ def update
82
+ run_callbacks :update do
83
+ attributes_to_save = self.changes.dup
84
+ attributes_to_save.each do |key, val|
85
+ attributes_to_save[key] = val.last
86
+ end
87
+
88
+ attributes_to_save.merge! off_band_changes
89
+
90
+ format = NaranyaEcm.options[:format] || 'json'
91
+ update_path = "#{self.path}.#{format}"
92
+
93
+ response = self.class.put update_path, persist_request_options(attributes_to_save)
94
+ case response.code
95
+ when 204
96
+ self.reload!
97
+ true
98
+ when 422 # Unprocessable Entity
99
+ self.load_server_errors(response.to_hash)
100
+ false
101
+ end
102
+ end
103
+ end
104
+
105
+
106
+ module ClassMethods
107
+
108
+ def create(attributes={})
109
+ object_to_create = self.new attributes
110
+ object_to_create.save
111
+ object_to_create
112
+ end
113
+ def create!(attributes={})
114
+ created_object = create(attributes)
115
+ raise "ValidationError" unless created_object.persisted?
116
+ created_object
117
+ end
118
+ end
119
+
120
+ end
121
+
122
+ end
@@ -0,0 +1,54 @@
1
+ module NaranyaEcm::Rest
2
+ class Relation
3
+ include Enumerable
4
+ delegate :to_yaml, :all?, :each, :first, :last, to: :to_a
5
+
6
+ attr_reader :klass, :conditions, :elements
7
+
8
+ def initialize(given_klass, given_conditions = {})
9
+ @klass, @conditions = given_klass, given_conditions.with_indifferent_access
10
+ end
11
+
12
+ def where(given_conditions = {})
13
+ spawn_chain(given_conditions)
14
+ end
15
+
16
+ def limit(given_limit)
17
+ spawn_chain(limit: given_limit.to_i)
18
+ end
19
+
20
+ def to_a
21
+ materialize! unless @elements
22
+ elements
23
+ end
24
+
25
+ def fetch_from_server
26
+ format = NaranyaEcm.options[:format] || 'json'
27
+ response = klass.get("#{klass.path}.#{format}", query: conditions)
28
+ case response.code
29
+ when 404
30
+ raise ResourceNotFound
31
+ when 200
32
+ response.to_a
33
+ end
34
+ end
35
+
36
+ def load(given_list)
37
+ # Revisar que todos los elementos sean Hash o la clase asociada:
38
+ raise "Type Mismatch: some elements are not a hash nor associated_class" unless given_list
39
+ .map { |e| e.is_a?(Hash) || e.is_a?(@klass) }
40
+ .reduce { |res, e| res && e }
41
+ @elements = given_list.map { |e| e.is_a?(Hash) ? @klass.load(e) : e }
42
+ end
43
+
44
+ private
45
+ def spawn_chain(given_conditions = {})
46
+ self.class.new klass, conditions.merge(given_conditions)
47
+ end
48
+
49
+ def materialize!
50
+ @elements = fetch_from_server.map { |s| klass.load s }
51
+ end
52
+
53
+ end
54
+ end
@@ -1,17 +1,22 @@
1
- module NaranyaEcm
2
- module Search
3
- class Hit
4
- attr_reader :id, :type, :updated_at, :order, :score
5
- attr_accessor :cache_key
6
- def initialize(attributes={})
7
- @id = attributes.delete :_id
8
- @type = attributes.delete :_type
9
- @score = attributes.delete :_score
1
+ module NaranyaEcm::Search
2
+ class Hit
3
+ attr_reader :id, :type, :updated_at, :order, :score
4
+ def initialize(attributes={})
5
+ @id = attributes.delete :_id
6
+ @type = attributes.delete :_type
7
+ @score = attributes.delete :_score
10
8
 
11
- source = attributes.delete :_source
12
- @updated_at = source.has_key?(:updated_at) ? source.delete(:updated_at).to_datetime : nil
13
- @order = attributes.delete :order
14
- end
9
+ source = attributes.delete :_source
10
+ @updated_at = source.has_key?(:updated_at) ? source.delete(:updated_at).to_datetime : nil
11
+ @order = attributes.delete :order
12
+ end
13
+
14
+ def klass
15
+ @klass ||= "NaranyaEcm::#{type.classify}".constantize
16
+ end
17
+
18
+ def cache_key
19
+ @cache_key ||= NaranyaEcm::Cache::Key.new klass, id, updated_at
15
20
  end
16
21
  end
17
- end
22
+ end
@@ -1,26 +1,24 @@
1
1
  require 'active_support/concern'
2
- module NaranyaEcm
3
- module Search
4
- module Methods
5
- extend ActiveSupport::Concern
2
+ module NaranyaEcm::Search
3
+ module Methods
4
+ extend ActiveSupport::Concern
6
5
 
7
- included do
8
- attr_reader :search_score, :search_order
9
- end
10
-
11
- def merge_search_hit_data(hit)
12
- @search_score, @search_order = hit.score, hit.order
13
- end
6
+ included do
7
+ attr_reader :search_score, :search_order
8
+ end
9
+
10
+ def merge_search_hit_data(hit)
11
+ @search_score, @search_order = hit.score, hit.order
12
+ end
14
13
 
15
- module ClassMethods
16
- def search(*args)
17
- Results.new(*args, self)
18
- end
19
- def kick_search(terms, options={})
20
- search(Query.build_searchkick_like(terms, options))
21
- end
14
+ module ClassMethods
15
+ def search(*args)
16
+ Results.new(self, *args)
17
+ end
18
+ def kick_search(terms, options={})
19
+ search(Query.build_searchkick_like(terms, options))
22
20
  end
23
-
24
21
  end
22
+
25
23
  end
26
- end
24
+ end