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.
- checksums.yaml +4 -4
- data/lib/naranya_ecm/behaviors/localizable.rb +27 -31
- data/lib/naranya_ecm/behaviors/mediable.rb +25 -0
- data/lib/naranya_ecm/behaviors/timestampable.rb +18 -15
- data/lib/naranya_ecm/cache/key.rb +32 -56
- data/lib/naranya_ecm/cache/methods.rb +50 -63
- data/lib/naranya_ecm/lifecycles/content_lifecycle.rb +39 -16
- data/lib/naranya_ecm/models/category.rb +11 -30
- data/lib/naranya_ecm/models/content.rb +73 -73
- data/lib/naranya_ecm/models/content_version.rb +50 -29
- data/lib/naranya_ecm/models/download_authorization.rb +22 -26
- data/lib/naranya_ecm/models/media_resource.rb +49 -15
- data/lib/naranya_ecm/rest/associations.rb +156 -0
- data/lib/naranya_ecm/rest/client.rb +4 -0
- data/lib/naranya_ecm/rest/errors.rb +53 -0
- data/lib/naranya_ecm/rest/finder_methods.rb +50 -0
- data/lib/naranya_ecm/rest/model.rb +215 -0
- data/lib/naranya_ecm/rest/persistence.rb +122 -0
- data/lib/naranya_ecm/rest/relation.rb +54 -0
- data/lib/naranya_ecm/search/hit.rb +19 -14
- data/lib/naranya_ecm/search/methods.rb +18 -20
- data/lib/naranya_ecm/search/query.rb +229 -230
- data/lib/naranya_ecm/search/results.rb +136 -139
- data/lib/naranya_ecm-sdk/version.rb +1 -1
- data/lib/naranya_ecm-sdk.rb +54 -13
- data/naranya_ecm-sdk.gemspec +1 -1
- data/spec/models/category_spec.rb +7 -2
- data/spec/models/content_spec.rb +11 -2
- data/spec/models/media_spec.rb +1 -1
- data/spec/spec_helper.rb +1 -1
- data/spec/support/naranya_ecms_shared_specs.rb +0 -12
- metadata +15 -19
- data/lib/naranya_ecm/behaviors/resourceable.rb +0 -22
- data/lib/naranya_ecm/behaviors.rb +0 -10
- data/lib/naranya_ecm/cache.rb +0 -9
- data/lib/naranya_ecm/has_many_patch.rb +0 -105
- data/lib/naranya_ecm/lifecycles/lifecycleable.rb +0 -43
- data/lib/naranya_ecm/lifecycles/version_lifecycle.rb +0 -75
- data/lib/naranya_ecm/lifecycles.rb +0 -10
- data/lib/naranya_ecm/models/embedded_hash.rb +0 -10
- data/lib/naranya_ecm/models/embedded_localized_hash.rb +0 -38
- data/lib/naranya_ecm/models/lifecycle.rb +0 -34
- data/lib/naranya_ecm/models.rb +0 -15
- 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
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
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
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
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
|
4
|
-
|
5
|
-
extend ActiveSupport::Concern
|
2
|
+
module NaranyaEcm::Search
|
3
|
+
module Methods
|
4
|
+
extend ActiveSupport::Concern
|
6
5
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
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
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
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
|