ext_ooor 2.3.0.1

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 (45) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +354 -0
  4. data/Rakefile +5 -0
  5. data/bin/ooor +43 -0
  6. data/lib/ext_ooor.rb +5 -0
  7. data/lib/ext_ooor/version.rb +5 -0
  8. data/lib/generators/ooor/install_generator.rb +18 -0
  9. data/lib/generators/ooor/ooor.yml +49 -0
  10. data/lib/ooor.rb +230 -0
  11. data/lib/ooor/associations.rb +78 -0
  12. data/lib/ooor/autosave_association.rb +197 -0
  13. data/lib/ooor/base.rb +130 -0
  14. data/lib/ooor/base64.rb +20 -0
  15. data/lib/ooor/callbacks.rb +18 -0
  16. data/lib/ooor/errors.rb +120 -0
  17. data/lib/ooor/field_methods.rb +213 -0
  18. data/lib/ooor/helpers/core_helpers.rb +83 -0
  19. data/lib/ooor/locale.rb +11 -0
  20. data/lib/ooor/mini_active_resource.rb +86 -0
  21. data/lib/ooor/model_registry.rb +24 -0
  22. data/lib/ooor/model_schema.rb +25 -0
  23. data/lib/ooor/naming.rb +92 -0
  24. data/lib/ooor/nested_attributes.rb +57 -0
  25. data/lib/ooor/persistence.rb +353 -0
  26. data/lib/ooor/rack.rb +137 -0
  27. data/lib/ooor/railtie.rb +27 -0
  28. data/lib/ooor/reflection.rb +151 -0
  29. data/lib/ooor/reflection_ooor.rb +121 -0
  30. data/lib/ooor/relation.rb +204 -0
  31. data/lib/ooor/relation/finder_methods.rb +153 -0
  32. data/lib/ooor/report.rb +53 -0
  33. data/lib/ooor/serialization.rb +49 -0
  34. data/lib/ooor/services.rb +134 -0
  35. data/lib/ooor/session.rb +250 -0
  36. data/lib/ooor/session_handler.rb +66 -0
  37. data/lib/ooor/transport.rb +34 -0
  38. data/lib/ooor/transport/json_client.rb +65 -0
  39. data/lib/ooor/transport/xml_rpc_client.rb +15 -0
  40. data/lib/ooor/type_casting.rb +223 -0
  41. data/lib/ooor/version.rb +8 -0
  42. data/spec/cli_spec.rb +129 -0
  43. data/spec/helpers/test_helper.rb +11 -0
  44. data/spec/ooor_spec.rb +867 -0
  45. metadata +118 -0
@@ -0,0 +1,66 @@
1
+ require 'active_support/core_ext/hash/indifferent_access'
2
+ require 'ooor/session'
3
+
4
+ module Ooor
5
+ autoload :SecureRandom, 'securerandom'
6
+ # The SessionHandler allows to retrieve a session with its loaded proxies to OpenERP
7
+ class SessionHandler
8
+
9
+ def noweb_session_spec(config)
10
+ "#{config[:url]}-#{config[:database]}-#{config[:username]}"
11
+ end
12
+
13
+ def retrieve_session(config, id=nil, web_session={})
14
+ id ||= SecureRandom.hex(16)
15
+ if id == :noweb
16
+ spec = noweb_session_spec(config)
17
+ else
18
+ spec = id
19
+ end
20
+
21
+ s = sessions[spec]
22
+ # reload session or create a new one if no matching session found
23
+ if config[:reload] || !s
24
+ config = Ooor.default_config.merge(config) if Ooor.default_config.is_a? Hash
25
+ Ooor::Session.new(config, web_session, id)
26
+
27
+ # found but config mismatch still
28
+ elsif noweb_session_spec(s.config) != noweb_session_spec(config)
29
+ config = Ooor.default_config.merge(config) if Ooor.default_config.is_a? Hash
30
+ Ooor::Session.new(config, web_session, id)
31
+
32
+ # matching session, update web_session of it eventually
33
+ else
34
+ s.tap {|s| s.web_session.merge!(web_session)} #TODO merge config also?
35
+ end
36
+ end
37
+
38
+ def register_session(session)
39
+ if session.config[:session_sharing]
40
+ spec = session.web_session[:session_id]
41
+ elsif session.id != :noweb
42
+ spec = session.id
43
+ else
44
+ spec = noweb_session_spec(session.config)
45
+ end
46
+ set_web_session(spec, session.web_session)
47
+ sessions[spec] = session
48
+ end
49
+
50
+ def reset!
51
+ @sessions = {}
52
+ @connections = {}
53
+ end
54
+
55
+ def get_web_session(key)
56
+ Ooor.cache.read(key)
57
+ end
58
+
59
+ def set_web_session(key, web_session)
60
+ Ooor.cache.write(key, web_session)
61
+ end
62
+
63
+ def sessions; @sessions ||= {}; end
64
+ def connections; @connections ||= {}; end
65
+ end
66
+ end
@@ -0,0 +1,34 @@
1
+ # OOOR: OpenObject On Ruby
2
+ # Copyright (C) 2013 Akretion LTDA (<http://www.akretion.com>).
3
+ # Author: Raphaël Valyi
4
+ # Licensed under the MIT license, see MIT-LICENSE file
5
+
6
+ require 'active_support/dependencies/autoload'
7
+
8
+ module Ooor
9
+ module Transport
10
+ extend ActiveSupport::Autoload
11
+ autoload :XmlRpcClient
12
+ autoload :JsonClient
13
+
14
+ def get_client(type, url)
15
+ case type
16
+ when :json
17
+ Thread.current[:json_clients] ||= {}
18
+ Thread.current[:json_clients][url] ||= JsonClient.new(url, :request => { timeout: config[:rpc_timeout] || 900 })
19
+ when :xml
20
+ Thread.current[:xml_clients] ||= {}
21
+ Thread.current[:xml_clients][url] ||= XmlRpcClient.new2(url, nil, config[:rpc_timeout] || 900)
22
+ end
23
+ end
24
+
25
+ def base_url
26
+ @base_url ||= config[:url] = "#{config[:url].gsub(/\/$/,'').chomp('/xmlrpc')}/xmlrpc"
27
+ end
28
+
29
+ def base_jsonrpc2_url
30
+ @base_jsonrpc2_url ||= config[:url].gsub(/\/$/,'').chomp('/xmlrpc')
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,65 @@
1
+ require 'faraday'
2
+
3
+ module Ooor
4
+ module Transport
5
+ module JsonClient
6
+ module OeAdapter # NOTE use a middleware here?
7
+
8
+ def oe_service(session_info, service, obj, method, *args)
9
+ if service == :exec_workflow
10
+ url = '/web/dataset/exec_workflow'
11
+ params = {"model"=>obj, "id"=>args[0], "signal"=>method}
12
+ elsif service == :db || service == :common
13
+ url = '/jsonrpc'
14
+ params = {"service"=> service, "method"=> method, "args"=> args}
15
+ elsif service == :execute
16
+ url = '/web/dataset/call_kw'
17
+ if (i = Ooor.irregular_context_position(method)) && args.size < i
18
+ kwargs = {"context"=> args[i]}
19
+ else
20
+ kwargs = {}
21
+ end
22
+ params = {"model"=>obj, "method"=> method, "kwargs"=> kwargs, "args"=>args}#, "context"=>context}
23
+ elsif service.to_s.start_with?("/") # assuming service URL is forced
24
+ url = service
25
+ params = args[0]
26
+ else
27
+ url = "/web/dataset/#{service}"
28
+ params = args[0].merge({"model"=>obj})
29
+ end # TODO reports for version > 7
30
+ oe_request(session_info, url, params, method, *args)
31
+ end
32
+
33
+ def oe_request(session_info, url, params, method, *args)
34
+ if session_info[:req_id]
35
+ session_info[:req_id] += 1
36
+ else
37
+ session_info[:req_id] = 1
38
+ end
39
+ if session_info[:sid] # required on v7 but forbidden in v8
40
+ params.merge!({"session_id" => session_info[:session_id]})
41
+ end
42
+ response = JSON.parse(post do |req|
43
+ req.headers['Cookie'] = session_info[:cookie] if session_info[:cookie]
44
+ req.url url
45
+ req.headers['Content-Type'] = 'application/json'
46
+ req.body = {"jsonrpc"=>"2.0","method"=>"call", "params" => params, "id"=>session_info[:req_id]}.to_json
47
+ end.body)
48
+ if response["error"]
49
+ faultCode = response["error"]['data']['fault_code'] || response["error"]['data']['debug']
50
+ raise OpenERPServerError.build(faultCode, response["error"]['message'], method, *args)
51
+ else
52
+ response["result"]
53
+ end
54
+ end
55
+ end
56
+
57
+ Faraday::Connection.send :include, OeAdapter
58
+
59
+ def self.new(url, options = {})
60
+ options[:ssl] = {:verify => false}
61
+ Faraday.new(url, options) # TODO use middlewares
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,15 @@
1
+ require 'xmlrpc/client'
2
+
3
+ module Ooor
4
+ module Transport
5
+ class XmlRpcClient < XMLRPC::Client
6
+ def call2(method, *args)
7
+ request = create().methodCall(method, *args)
8
+ data = (["<?xml version='1.0' encoding='UTF-8'?>\n"] + do_rpc(request, false).lines.to_a[1..-1]).join #encoding is not defined by OpenERP and can lead to bug with Ruby 1.9
9
+ parser().parseMethodResponse(data)
10
+ rescue RuntimeError => e
11
+ raise OpenERPServerError.create_from_trace(e, method, *args)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,223 @@
1
+ # OOOR: OpenObject On Ruby
2
+ # Copyright (C) 2009-2014 Akretion LTDA (<http://www.akretion.com>).
3
+ # Author: Raphaël Valyi
4
+ # Licensed under the MIT license, see MIT-LICENSE file
5
+
6
+ module Ooor
7
+ module TypeCasting
8
+ extend ActiveSupport::Concern
9
+
10
+ OPERATORS = ["=", "!=", "<=", "<", ">", ">=", "=?", "=like", "=ilike", "like", "not like", "ilike", "not ilike", "in", "not in", "child_of"]
11
+ BLACKLIST = %w[id write_date create_date write_ui create_ui]
12
+
13
+ module ClassMethods
14
+
15
+ def openerp_string_domain_to_ruby(string_domain) #FIXME: used? broken?
16
+ eval(string_domain.gsub('(', '[').gsub(')',']'))
17
+ end
18
+
19
+ def to_openerp_domain(domain)
20
+ if domain.is_a?(Hash)
21
+ return domain.map{|k,v| [k.to_s, '=', v]}
22
+ elsif domain == []
23
+ return []
24
+ elsif domain.is_a?(Array) && !domain.last.is_a?(Array)
25
+ return [domain]
26
+ else
27
+ return domain
28
+ end
29
+ end
30
+
31
+ def to_rails_type(type)
32
+ case type.to_sym
33
+ when :char
34
+ :string
35
+ when :binary
36
+ :file
37
+ when :many2one
38
+ :belongs_to
39
+ when :one2many
40
+ :has_many
41
+ when :many2many
42
+ :has_and_belongs_to_many
43
+ else
44
+ type.to_sym
45
+ end
46
+ end
47
+
48
+ def value_to_openerp(v)
49
+ if v == nil || v == ""
50
+ return false
51
+ elsif !v.is_a?(Integer) && !v.is_a?(Float) && v.is_a?(Numeric) && v.respond_to?(:to_f)
52
+ return v.to_f
53
+ elsif !v.is_a?(Numeric) && !v.is_a?(Integer) && v.respond_to?(:sec) && v.respond_to?(:year)#really ensure that's a datetime type
54
+ return "%d-%02d-%02d %02d:%02d:%02d" % [v.year, v.month, v.day, v.hour, v.min, v.sec]
55
+ elsif !v.is_a?(Numeric) && !v.is_a?(Integer) && v.respond_to?(:day) && v.respond_to?(:year)#really ensure that's a date type
56
+ return "%d-%02d-%02d" % [v.year, v.month, v.day]
57
+ elsif v == "false" #may happen with OOORBIT
58
+ return false
59
+ elsif v.respond_to?(:read)
60
+ return Base64.encode64(v.read())
61
+ else
62
+ v
63
+ end
64
+ end
65
+
66
+ def cast_request_to_openerp(request)
67
+ if request.is_a?(Array)
68
+ request.map { |item| cast_request_to_openerp(item) }
69
+ elsif request.is_a?(Hash)
70
+ request2 = {}
71
+ request.each do |k, v|
72
+ request2[k] = cast_request_to_openerp(v)
73
+ end
74
+ else
75
+ value_to_openerp(request)
76
+ end
77
+ end
78
+
79
+ def cast_answer_to_ruby!(answer)
80
+ def cast_map_to_ruby!(map)
81
+ map.each do |k, v|
82
+ if self.t.fields[k] && v.is_a?(String) && !v.empty?
83
+ case self.t.fields[k]['type']
84
+ when 'datetime'
85
+ map[k] = DateTime.parse(v)
86
+ when 'date'
87
+ map[k] = Date.parse(v)
88
+ end
89
+ end
90
+ end
91
+ end
92
+
93
+ if answer.is_a?(Array)
94
+ answer.each {|item| self.cast_map_to_ruby!(item) if item.is_a? Hash}
95
+ elsif answer.is_a?(Hash)
96
+ self.cast_map_to_ruby!(answer)
97
+ else
98
+ answer
99
+ end
100
+ end
101
+
102
+ end
103
+
104
+ def sanitize_attribute(skey, value)
105
+ type = self.class.fields[skey]['type']
106
+ if type == 'boolean' && (value == 1 || value == "1")
107
+ true
108
+ elsif type == 'boolean'&& (value == 0 || value == "0")
109
+ false
110
+ elsif value == false && type != 'boolean'
111
+ nil
112
+ elsif (type == 'char' || type == 'text') && value == "" && @attributes[skey] == nil
113
+ nil
114
+ else
115
+ value
116
+ end
117
+ end
118
+
119
+ def sanitize_association(skey, value)
120
+ if value.is_a?(Ooor::Base) || value.is_a?(Array) && value.all? {|i| i.is_a?(Ooor::Base)}
121
+ value
122
+ elsif value.is_a?(Array) && !self.class.many2one_associations.keys.index(skey)
123
+ value.reject {|i| i == ''}.map {|i| i.is_a?(String) ? i.to_i : i}
124
+ elsif value.is_a?(String)
125
+ sanitize_association_as_string(skey, value)
126
+ else
127
+ value
128
+ end
129
+ end
130
+
131
+ def sanitize_association_as_string(skey, value)
132
+ if self.class.polymorphic_m2o_associations.has_key?(skey)
133
+ value
134
+ elsif self.class.many2one_associations.has_key?(skey)
135
+ sanitize_m2o_association(value)
136
+ else
137
+ value.split(",").map {|i| i.to_i}
138
+ end
139
+ end
140
+
141
+ def sanitize_m2o_association(value)
142
+ if value.blank? || value == "0"
143
+ false
144
+ else
145
+ value.to_i
146
+ end
147
+ end
148
+
149
+ def to_openerp_hash
150
+ attribute_keys, association_keys = get_changed_values
151
+ associations = {}
152
+ association_keys.each { |k| associations[k] = self.cast_association(k) }
153
+ @attributes.slice(*attribute_keys).merge(associations)
154
+ end
155
+
156
+ def get_changed_values
157
+ attribute_keys = changed.select {|k| self.class.fields.has_key?(k)} - BLACKLIST
158
+ association_keys = changed.select {|k| self.class.associations_keys.index(k)}
159
+ return attribute_keys, association_keys
160
+ end
161
+
162
+ # talk OpenERP cryptic associations API
163
+ def cast_association(k)
164
+ if self.class.one2many_associations[k]
165
+ if @loaded_associations[k]
166
+ v = @loaded_associations[k].select {|i| i.changed?}
167
+ v = @associations[k] if v.empty?
168
+ else
169
+ v = @associations[k]
170
+ end
171
+ cast_o2m_association(v)
172
+ elsif self.class.many2many_associations[k]
173
+ v = @associations[k]
174
+ [[6, false, (v || []).map {|i| i.is_a?(Base) ? i.id : i}]]
175
+ elsif self.class.many2one_associations[k]
176
+ v = @associations[k] # TODO support for nested
177
+ cast_m2o_association(v)
178
+ end
179
+ end
180
+
181
+ def cast_o2m_association(v)
182
+ v.collect do |value|
183
+ if value.is_a?(Base)
184
+ if value.new_record?
185
+ [0, 0, value.to_openerp_hash]
186
+ elsif value.marked_for_destruction?
187
+ [2, value.id]
188
+ elsif value.id
189
+ [1, value.id , value.to_openerp_hash]
190
+ end
191
+ elsif value.is_a?(Hash)
192
+ [0, 0, value]
193
+ else #use case?
194
+ [1, value, {}]
195
+ end
196
+ end
197
+ end
198
+
199
+ def cast_o2m_nested_attributes(v)
200
+ v.keys.collect do |key|
201
+ val = v[key]
202
+ if !val["_destroy"].blank?
203
+ [2, val[:id].to_i || val['id']]
204
+ elsif val[:id] || val['id']
205
+ [1, val[:id].to_i || val['id'], val]
206
+ else
207
+ [0, 0, val]
208
+ end
209
+ end
210
+ end
211
+
212
+ def cast_m2o_association(v)
213
+ if v.is_a?(Array)
214
+ v[0]
215
+ elsif v.is_a?(Base)
216
+ v.id
217
+ else
218
+ v
219
+ end
220
+ end
221
+
222
+ end
223
+ end
@@ -0,0 +1,8 @@
1
+ module Ooor
2
+ MAJOR = 2
3
+ MINOR = 3
4
+ TINY = 0
5
+ PRE = nil
6
+
7
+ VERSION = [MAJOR, MINOR, TINY].compact.join('.')
8
+ end
@@ -0,0 +1,129 @@
1
+ # Copyright (C) 2017 Akretion (<http://www.akretion.com>).
2
+ # Author: Raphaël Valyi
3
+ # Licensed under the MIT license, see MIT-LICENSE file
4
+
5
+ if ENV["CI"]
6
+ require 'coveralls'
7
+ Coveralls.wear!
8
+ end
9
+ require File.dirname(__FILE__) + '/../lib/ooor'
10
+
11
+ OOOR_URL = ENV['OOOR_URL'] || 'http://localhost:8069'
12
+ OOOR_DB_PASSWORD = ENV['OOOR_DB_PASSWORD'] || 'admin'
13
+ OOOR_USERNAME = ENV['OOOR_USERNAME'] || 'admin'
14
+ OOOR_PASSWORD = ENV['OOOR_PASSWORD'] || 'admin'
15
+ OOOR_DATABASE = ENV['OOOR_DATABASE'] || 'ooor_test'
16
+ OOOR_ODOO_VERSION = ENV['VERSION'] || '10.0'
17
+
18
+
19
+ # Note: we never set both teh password and the database to avoid invalid logins here
20
+ describe "ooor CLI" do
21
+
22
+ before do
23
+ ENV['OOOR_URL'] = nil # removed to avoid conflicting with Ooor.new tests
24
+ ENV['OOOR_DATABASE'] = nil
25
+ end
26
+
27
+ after do
28
+ ENV['OOOR_URL'] = OOOR_URL
29
+ ENV['OOOR_DATABASE'] = OOOR_DATABASE
30
+ end
31
+
32
+ describe "ooor 2.x legacy format" do
33
+ it "should parse user.mydb@myhost:8089" do
34
+ Ooor.new("user.mydb@myhost:8089")
35
+ s = Ooor.default_session
36
+ expect(s.config.username).to eq 'user'
37
+ expect(s.config.database).to eq 'mydb'
38
+ expect(s.config.url).to eq 'http://myhost:8089'
39
+ end
40
+
41
+ it "should parse user.mydb@myhost:443" do
42
+ Ooor.new("user.mydb@myhost:443")
43
+ s = Ooor.default_session
44
+ expect(s.config.username).to eq 'user'
45
+ expect(s.config.database).to eq 'mydb'
46
+ expect(s.config.url).to eq 'https://myhost:443'
47
+ end
48
+
49
+ it "should parse user.mydb@myhost:8089 -s" do
50
+ Ooor.new("user.mydb@myhost:8089 -s")
51
+ s = Ooor.default_session
52
+ expect(s.config.username).to eq 'user'
53
+ expect(s.config.database).to eq 'mydb'
54
+ expect(s.config.url).to eq 'https://myhost:8089'
55
+ end
56
+ end
57
+
58
+
59
+ describe "new connection string format" do
60
+ it "ooor://user@myhost" do
61
+ Ooor.new("ooor://user@myhost")
62
+ s = Ooor.default_session
63
+ expect(s.config.username).to eq 'user'
64
+ expect(s.config.url).to eq 'http://myhost:80'
65
+ end
66
+
67
+ it "ooor://user@myhost:8089" do
68
+ Ooor.new("ooor://user@myhost:8089")
69
+ s = Ooor.default_session
70
+ expect(s.config.username).to eq 'user'
71
+ expect(s.config.url).to eq 'http://myhost:8089'
72
+ end
73
+
74
+ it "ooor://user:secret@myhost" do
75
+ Ooor.session_handler.reset!
76
+ Ooor.new("ooor://user:secret@myhost")
77
+ s = Ooor.default_session
78
+ expect(s.config.username).to eq 'user'
79
+ expect(s.config.password).to eq 'secret'
80
+ expect(s.config.url).to eq 'http://myhost:80'
81
+ end
82
+
83
+ it "ooor://user:secret@myhost/mydb" do
84
+ Ooor.session_handler.reset!
85
+ config = Ooor.format_config("ooor://user:secret@myhost/mydb")
86
+ expect(config[:username]).to eq 'user'
87
+ expect(config[:password]).to eq 'secret'
88
+ expect(config[:database]).to eq 'mydb'
89
+ expect(config[:url]).to eq 'http://myhost:80'
90
+ end
91
+
92
+ it "ooor://user:secret@myhost:8089" do
93
+ Ooor.session_handler.reset!
94
+ Ooor.new("ooor://user:secret@myhost:8089")
95
+ s = Ooor.default_session
96
+ expect(s.config.username).to eq 'user'
97
+ expect(s.config.password).to eq 'secret'
98
+ expect(s.config.url).to eq 'http://myhost:8089'
99
+ end
100
+
101
+ it "ooor://user@myhost:8089/mydb" do
102
+ Ooor.session_handler.reset!
103
+ Ooor.new("ooor://user@myhost:8089/mydb")
104
+ s = Ooor.default_session
105
+ expect(s.config.username).to eq 'user'
106
+ expect(s.config.database).to eq 'mydb'
107
+ expect(s.config.url).to eq 'http://myhost:8089'
108
+ end
109
+
110
+ it "user:secret@myhost:8089" do
111
+ Ooor.session_handler.reset!
112
+ Ooor.new("user:secret@myhost:8089")
113
+ s = Ooor.default_session
114
+ expect(s.config.username).to eq 'user'
115
+ expect(s.config.password).to eq 'secret'
116
+ expect(s.config.url).to eq 'http://myhost:8089'
117
+ end
118
+
119
+ it "ooor://myhost.com:8089/mydb?ssl=true" do
120
+ Ooor.session_handler.reset!
121
+ Ooor.new("ooor://myhost.com:8089/mydb?ssl=true")
122
+ s = Ooor.default_session
123
+ expect(s.config.username).to eq 'admin'
124
+ expect(s.config.database).to eq 'mydb'
125
+ expect(s.config.url).to eq 'https://myhost.com:8089'
126
+ end
127
+
128
+ end
129
+ end