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
@@ -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