ooor 1.9.2 → 2.0.0

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. data/README.md +23 -71
  2. data/Rakefile +5 -0
  3. data/bin/ooor +1 -0
  4. data/lib/ooor.rb +87 -129
  5. data/lib/ooor/associations.rb +64 -0
  6. data/lib/ooor/base.rb +218 -0
  7. data/lib/{app/models → ooor}/base64.rb +0 -0
  8. data/lib/ooor/connection.rb +37 -0
  9. data/lib/ooor/errors.rb +120 -0
  10. data/lib/ooor/field_methods.rb +153 -0
  11. data/lib/{app → ooor}/helpers/core_helpers.rb +2 -2
  12. data/lib/ooor/locale.rb +13 -0
  13. data/lib/ooor/mini_active_resource.rb +94 -0
  14. data/lib/ooor/model_registry.rb +19 -0
  15. data/lib/ooor/naming.rb +73 -0
  16. data/lib/ooor/rack.rb +114 -0
  17. data/lib/ooor/railtie.rb +41 -0
  18. data/lib/ooor/reflection.rb +537 -0
  19. data/lib/ooor/reflection_ooor.rb +92 -0
  20. data/lib/{app/models → ooor}/relation.rb +61 -22
  21. data/lib/ooor/relation/finder_methods.rb +113 -0
  22. data/lib/ooor/report.rb +53 -0
  23. data/lib/{app/models → ooor}/serialization.rb +18 -6
  24. data/lib/ooor/services.rb +133 -0
  25. data/lib/ooor/session.rb +120 -0
  26. data/lib/ooor/session_handler.rb +63 -0
  27. data/lib/ooor/transport.rb +34 -0
  28. data/lib/ooor/transport/json_client.rb +53 -0
  29. data/lib/ooor/transport/xml_rpc_client.rb +15 -0
  30. data/lib/ooor/type_casting.rb +193 -0
  31. data/lib/ooor/version.rb +8 -0
  32. data/spec/helpers/test_helper.rb +11 -0
  33. data/spec/install_nightly.sh +17 -0
  34. data/spec/ooor_spec.rb +197 -79
  35. data/spec/requirements.txt +19 -0
  36. metadata +58 -20
  37. data/lib/app/models/client_xmlrpc.rb +0 -34
  38. data/lib/app/models/open_object_resource.rb +0 -486
  39. data/lib/app/models/services.rb +0 -47
  40. data/lib/app/models/type_casting.rb +0 -134
  41. data/lib/app/models/uml.rb +0 -210
  42. data/lib/app/ui/action_window.rb +0 -96
  43. data/lib/app/ui/client_base.rb +0 -36
  44. data/lib/app/ui/form_model.rb +0 -88
  45. data/ooor.yml +0 -27
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ooor
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.9.2
4
+ version: 2.0.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -12,7 +12,7 @@ cert_chain: []
12
12
  date: 2013-05-04 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
- name: activeresource
15
+ name: activemodel
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
@@ -27,34 +27,68 @@ dependencies:
27
27
  - - ! '>='
28
28
  - !ruby/object:Gem::Version
29
29
  version: 2.3.5
30
+ - !ruby/object:Gem::Dependency
31
+ name: faraday
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
30
46
  description: OOOR exposes OpenERP business object proxies to your Ruby (Rails or not)
31
47
  application. It extends the standard ActiveResource API. Running on JRuby, OOOR
32
48
  also offers a convenient bridge between OpenERP and the Java eco-system
33
- email: rvalyi@akretion.com
49
+ email: raphael.valyi@akretion.com
34
50
  executables:
35
51
  - ooor
36
52
  extensions: []
37
53
  extra_rdoc_files: []
38
54
  files:
39
- - README.md
40
- - MIT-LICENSE
41
55
  - lib/ooor.rb
42
- - ooor.yml
43
- - lib/app/models/open_object_resource.rb
44
- - lib/app/models/type_casting.rb
45
- - lib/app/models/serialization.rb
46
- - lib/app/models/uml.rb
47
- - lib/app/models/base64.rb
48
- - lib/app/models/client_xmlrpc.rb
49
- - lib/app/models/relation.rb
50
- - lib/app/models/services.rb
51
- - lib/app/ui/action_window.rb
52
- - lib/app/ui/client_base.rb
53
- - lib/app/ui/form_model.rb
54
- - lib/app/helpers/core_helpers.rb
56
+ - lib/ooor/naming.rb
57
+ - lib/ooor/session.rb
58
+ - lib/ooor/model_registry.rb
59
+ - lib/ooor/relation.rb
60
+ - lib/ooor/relation/finder_methods.rb
61
+ - lib/ooor/reflection_ooor.rb
62
+ - lib/ooor/connection.rb
63
+ - lib/ooor/report.rb
64
+ - lib/ooor/errors.rb
65
+ - lib/ooor/transport.rb
66
+ - lib/ooor/associations.rb
67
+ - lib/ooor/field_methods.rb
68
+ - lib/ooor/serialization.rb
69
+ - lib/ooor/base64.rb
70
+ - lib/ooor/locale.rb
71
+ - lib/ooor/version.rb
72
+ - lib/ooor/reflection.rb
73
+ - lib/ooor/helpers/core_helpers.rb
74
+ - lib/ooor/railtie.rb
75
+ - lib/ooor/rack.rb
76
+ - lib/ooor/mini_active_resource.rb
77
+ - lib/ooor/type_casting.rb
78
+ - lib/ooor/services.rb
79
+ - lib/ooor/session_handler.rb
80
+ - lib/ooor/transport/json_client.rb
81
+ - lib/ooor/transport/xml_rpc_client.rb
82
+ - lib/ooor/base.rb
83
+ - MIT-LICENSE
84
+ - README.md
85
+ - Rakefile
86
+ - spec/requirements.txt
87
+ - spec/helpers/test_helper.rb
88
+ - spec/install_nightly.sh
55
89
  - spec/ooor_spec.rb
56
90
  - bin/ooor
57
- homepage: http://github.com/rvalyi/ooor
91
+ homepage: http://github.com/akretion/ooor
58
92
  licenses: []
59
93
  post_install_message:
60
94
  rdoc_options: []
@@ -78,4 +112,8 @@ rubygems_version: 1.8.24
78
112
  signing_key:
79
113
  specification_version: 3
80
114
  summary: OOOR - OpenObject On Ruby
81
- test_files: []
115
+ test_files:
116
+ - spec/requirements.txt
117
+ - spec/helpers/test_helper.rb
118
+ - spec/install_nightly.sh
119
+ - spec/ooor_spec.rb
@@ -1,34 +0,0 @@
1
- # OOOR: OpenObject On Ruby
2
- # Copyright (C) 2009-2012 Akretion LTDA (<http://www.akretion.com>).
3
- # Author: Raphaël Valyi
4
- # Licensed under the MIT license, see MIT-LICENSE file
5
-
6
- require 'xmlrpc/client'
7
-
8
- module Ooor
9
- class XMLClient < XMLRPC::Client
10
- def self.new2(ooor, url, p, timeout)
11
- @ooor = ooor
12
- super(url, p, timeout)
13
- end
14
-
15
- def call2(method, *args)
16
- request = create().methodCall(method, *args)
17
- 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
18
- parser().parseMethodResponse(data)
19
- rescue RuntimeError => e
20
- begin
21
- #extracts the eventual error log from OpenERP response as OpenERP doesn't enforce carefully*
22
- #the XML/RPC spec, see https://bugs.launchpad.net/openerp/+bug/257581
23
- openerp_error_hash = eval("#{ e }".gsub("wrong fault-structure: ", ""))
24
- rescue SyntaxError
25
- raise e
26
- end
27
- if openerp_error_hash.is_a? Hash
28
- raise RuntimeError.new "\n\n*********** OpenERP Server ERROR ***********\n#{openerp_error_hash["faultCode"]}\n#{openerp_error_hash["faultString"]}********************************************\n."
29
- else
30
- raise e
31
- end
32
- end
33
- end
34
- end
@@ -1,486 +0,0 @@
1
- # OOOR: OpenObject On Ruby
2
- # Copyright (C) 2009-2012 Akretion LTDA (<http://www.akretion.com>).
3
- # Author: Raphaël Valyi
4
- # Licensed under the MIT license, see MIT-LICENSE file
5
-
6
- require 'rubygems'
7
- require 'active_resource'
8
- require 'app/ui/form_model'
9
- require 'app/models/uml'
10
- require 'app/models/type_casting'
11
- require 'app/models/relation'
12
- require 'app/models/serialization'
13
-
14
- autoload :UML, 'app/models/uml'
15
-
16
- module Ooor
17
- class OpenObjectResource < ActiveResource::Base
18
- #PREDEFINED_INHERITS = {'product.product' => 'product_tmpl_id'}
19
- #include ActiveModel::Validations
20
- include TypeCasting
21
- include Serialization
22
-
23
- # ******************** class methods ********************
24
- class << self
25
-
26
- cattr_accessor :logger
27
- attr_accessor :openerp_id, :info, :access_ids, :name, :description, :openerp_model, :field_ids, :state, #class attributes associated to the OpenERP ir.model
28
- :fields, :fields_defined, :many2one_associations, :one2many_associations, :many2many_associations, :polymorphic_m2o_associations, :associations_keys,
29
- :database, :user_id, :scope_prefix, :ooor, :association
30
-
31
- def model_name
32
- @_model_name ||= begin
33
- namespace = self.parents.detect do |n|
34
- n.respond_to?(:use_relative_model_naming?) && n.use_relative_model_naming?
35
- end
36
- ActiveModel::Name.new(self, namespace, description)
37
- end
38
- end
39
-
40
- def print_uml(options={})
41
- UML.print_uml([self], options)
42
- end
43
-
44
- def class_name_from_model_key(model_key=self.openerp_model)
45
- model_key.split('.').collect {|name_part| name_part.capitalize}.join
46
- end
47
-
48
- #similar to Object#const_get but for OpenERP model key
49
- def const_get(model_key, context={})
50
- klass_name = class_name_from_model_key(model_key)
51
- klass = (self.scope_prefix ? Object.const_get(self.scope_prefix) : Object).const_defined?(klass_name) ? (self.scope_prefix ? Object.const_get(self.scope_prefix) : Object).const_get(klass_name) : @ooor.define_openerp_model({'model' => model_key}, self.scope_prefix)
52
- klass.reload_fields_definition(false, context)
53
- klass
54
- end
55
-
56
- def create(attributes = {}, context={}, default_get_list=false, reload=true)
57
- self.new(attributes, default_get_list, context).tap { |resource| resource.save(context, reload) }
58
- end
59
-
60
- def reload_fields_definition(force=false, context={})
61
- if force or not @fields_defined
62
- @fields_defined = true
63
- @fields = {}
64
- rpc_execute("fields_get").each do |k, field|
65
- case field['type']
66
- when 'many2one'
67
- @many2one_associations[k] = field
68
- when 'one2many'
69
- @one2many_associations[k] = field
70
- when 'many2many'
71
- @many2many_associations[k] = field
72
- when 'reference'
73
- @polymorphic_m2o_associations[k] = field
74
- else
75
- # if ['integer', 'int8'].index(field['type'])
76
- # self.send :validates_numericality_of, k, :only_integer => true
77
- # elsif field['type'] == 'float'
78
- # self.send :validates_numericality_of, k
79
- # elsif field['type'] == 'char'
80
- # self.send :validates_length_of, k, :maximum => field['size'] || 128
81
- # end
82
- @fields[k] = field if field['name'] != 'id'
83
- end
84
- # if field["required"]
85
- # if field['type'] == 'many2one'
86
- # next if PREDEFINED_INHERITS[self.openerp_model] == k
87
- # end
88
- # self.send :validates_presence_of, k
89
- # end
90
- end
91
- @associations_keys = @many2one_associations.keys + @one2many_associations.keys + @many2many_associations.keys + @polymorphic_m2o_associations.keys
92
- (@fields.keys + @associations_keys).each do |meth| #generates method handlers for auto-completion tools such as jirb_swing
93
- unless self.respond_to?(meth)
94
- self.instance_eval do
95
- define_method meth do |*args|
96
- self.send :method_missing, *[meth, *args]
97
- end
98
- end
99
- end
100
- end
101
- logger.debug "#{fields.size} fields loaded in model #{self.name}"
102
- end
103
- end
104
-
105
- # ******************** remote communication ********************
106
-
107
- #OpenERP search method
108
- def search(domain=[], offset=0, limit=false, order=false, context={}, count=false)
109
- rpc_execute('search', to_openerp_domain(domain), offset, limit, order, context, count)
110
- end
111
-
112
- def relation; @relation ||= Relation.new(self); end
113
- def where(opts, *rest); relation.where(opts, *rest); end
114
- def all(*args); relation.all(*args); end
115
- def limit(value); relation.limit(value); end
116
- def order(value); relation.order(value); end
117
- def offset(value); relation.offset(value); end
118
-
119
- #corresponding method for OpenERP osv.execute(self, db, uid, obj, method, *args, **kw) method
120
- def rpc_execute(method, *args)
121
- rpc_execute_with_object(@openerp_model, method, *args)
122
- end
123
-
124
- def rpc_execute_with_object(object, method, *args)
125
- if args[-1].is_a? Hash #context
126
- user_id = args[-1].delete(:user_id) || args[-1].delete('user_id') || @user_id || @ooor.config[:user_id]
127
- password = args[-1].delete(:password) || args[-1].delete('password') || @password || @ooor.config[:password]
128
- database = args[-1].delete(:database) || args[-1].delete('database') || @database || @ooor.config[:database]
129
- args[-1].delete(:context)
130
- else
131
- user_id = @user_id || @ooor.config[:user_id] #TODO @user_id useless?
132
- password = @password || @ooor.config[:password]
133
- database = @database || @ooor.config[:database]
134
- end
135
- rpc_execute_with_all(database, user_id, password, object, method, *args)
136
- end
137
-
138
- #corresponding method for OpenERP osv.execute(self, db, uid, obj, method, *args, **kw) method
139
- def rpc_execute_with_all(db, uid, pass, obj, method, *args)
140
- clean_request_args!(args)
141
- reload_fields_definition(false, {:user_id => uid, :password => pass})
142
- logger.debug "OOOR RPC: rpc_method: 'execute', db: #{db}, uid: #{uid}, pass: #, obj: #{obj}, method: #{method}, *args: #{args.inspect}"
143
- cast_answer_to_ruby!(@ooor.get_rpc_client("#{(@database && @site || @ooor.base_url)}/object").call("execute", db, uid, pass, obj, method, *args))
144
- end
145
-
146
- #corresponding method for OpenERP osv.exec_workflow(self, db, uid, obj, method, *args)
147
- def rpc_exec_workflow(action, *args)
148
- rpc_exec_workflow_with_object(@openerp_model, action, *args)
149
- end
150
-
151
- def rpc_exec_workflow_with_object(object, action, *args)
152
- rpc_exec_workflow_with_all(@database || @ooor.config[:database], @user_id || @ooor.config[:user_id], @password || @ooor.config[:password], object, action, *args)
153
- end
154
-
155
- def rpc_exec_workflow_with_all(db, uid, pass, obj, action, *args)
156
- clean_request_args!(args)
157
- reload_fields_definition()
158
- logger.debug "OOOR RPC: 'exec_workflow', db: #{db}, uid: #{uid}, pass: #, obj: #{obj}, action: #{action}, *args: #{args.inspect}"
159
- cast_answer_to_ruby!(@ooor.get_rpc_client("#{(@database && @site || @ooor.base_url)}/object").call("exec_workflow", db, uid, pass, obj, action, *args))
160
- end
161
-
162
- def old_wizard_step(wizard_name, ids, step='init', wizard_id=nil, form={}, context={}, report_type='pdf')
163
- context = @ooor.global_context.merge(context)
164
- cast_map_to_openerp!(form)
165
- unless wizard_id
166
- logger.debug "OOOR RPC: 'create old_wizard_step' #{wizard_name}"
167
- wizard_id = cast_answer_to_ruby!(@ooor.get_rpc_client((@database && @site || @ooor.base_url) + "/wizard").call("create", @database || @ooor.config[:database], @user_id || @ooor.config[:user_id], @password || @ooor.config[:password], wizard_name))
168
- end
169
- params = {'model' => @openerp_model, 'form' => form, 'report_type' => report_type}
170
- params.merge!({'id' => ids[0], 'ids' => ids}) if ids
171
- logger.debug "OOOR RPC: 'execute old_wizard_step' #{wizard_id}, #{params.inspect}, #{step}, #{context}"
172
- [wizard_id, cast_answer_to_ruby!(@ooor.get_rpc_client("#{(@database && @site || @ooor.base_url)}/wizard").call("execute", @database || @ooor.config[:database], @user_id || @ooor.config[:user_id], @password || @ooor.config[:password], wizard_id, params, step, context))]
173
- end
174
-
175
- def method_missing(method_symbol, *arguments)
176
- raise RuntimeError.new("Invalid RPC method: #{method_symbol}") if [:type!, :allowed!].index(method_symbol)
177
- self.rpc_execute(method_symbol.to_s, *arguments)
178
- end
179
-
180
- #Added methods to obtain report data for a model
181
- def report(report_name, ids, report_type='pdf', context={})
182
- context = @ooor.global_context.merge(context)
183
- params = {'model' => @openerp_model, 'id' => ids[0], 'report_type' => report_type}
184
- @ooor.get_rpc_client("#{(@database && @site || @ooor.base_url)}/report").call("report", @database || @ooor.config[:database], @user_id || @ooor.config[:user_id], @password || @ooor.config[:password], report_name, ids, params, context)
185
- end
186
-
187
- def report_get(report_id, context={})
188
- context = @ooor.global_context.merge(context)
189
- @ooor.get_rpc_client("#{(@database && @site || @ooor.base_url)}/report").call("report_get", @database || @ooor.config[:database], @user_id || @ooor.config[:user_id], @password || @ooor.config[:password], report_id)
190
- end
191
-
192
- def get_report_data(report_name, ids, report_type='pdf', context={})
193
- report_id = self.report(report_name, ids, report_type, context)
194
- if report_id
195
- state = false
196
- attempt = 0
197
- while not state
198
- report = self.report_get(report_id, context)
199
- state = report["state"]
200
- attempt = 1
201
- if not state
202
- sleep(0.1)
203
- attempt += 1
204
- else
205
- return [report["result"],report["format"]]
206
- end
207
- if attempt > 100
208
- logger.debug "OOOR RPC: 'Printing Aborted!'"
209
- break
210
- end
211
- end
212
- else
213
- logger.debug "OOOR RPC: 'report not found'"
214
- end
215
- return nil
216
- end
217
-
218
- def find(*arguments)
219
- scope = arguments.slice!(0)
220
- options = arguments.slice!(0) || {}
221
- case scope
222
- when :all then find_every(options)
223
- when :first then find_every(options.merge(:limit => 1)).first
224
- when :last then find_every(options).last
225
- when :one then find_one(options)
226
- else find_single(scope, options)
227
- end
228
- end
229
-
230
- #******************** finders low level implementation ********************
231
- private
232
-
233
- def find_every(options)
234
- domain = options[:domain] || []
235
- context = options[:context] || {}
236
- #prefix_options, domain = split_options(options[:params]) unless domain
237
- ids = rpc_execute('search', to_openerp_domain(domain), options[:offset] || 0, options[:limit] || false, options[:order] || false, context.dup)
238
- !ids.empty? && ids[0].is_a?(Integer) && find_single(ids, options) || []
239
- end
240
-
241
- #actually finds many resources specified with scope = ids_array
242
- def find_single(scope, options)
243
- context = options[:context] || {}
244
- reload_fields_definition(false, context)
245
- all_fields = @fields.merge(@many2one_associations).merge(@one2many_associations).merge(@many2many_associations).merge(@polymorphic_m2o_associations)
246
- fields = options[:fields] || options[:only] || all_fields.keys.select {|k| all_fields[k]["type"] != "binary" && (options[:include_functions] || !all_fields[k]["function"])}
247
- # prefix_options, query_options = split_options(options[:params])
248
- is_collection = true
249
- scope = [scope] and is_collection = false if !scope.is_a? Array
250
- scope.map! do |item|
251
- if item.is_a?(String) && item.to_i == 0#triggers ir_model_data absolute reference lookup
252
- tab = item.split(".")
253
- domain = [['name', '=', tab[-1]]]
254
- domain += [['module', '=', tab[-2]]] if tab[-2]
255
- ir_model_data = const_get('ir.model.data', context).find(:first, :domain => domain)
256
- ir_model_data && ir_model_data.res_id && search([['id', '=', ir_model_data.res_id]])[0]
257
- else
258
- item
259
- end
260
- end.reject! {|item| !item}
261
- records = rpc_execute('read', scope, fields, context.dup)
262
- records = records.sort_by {|r| scope.index(r["id"])} #TODO use sort_by! in Ruby 1.9
263
- active_resources = []
264
- records.each do |record|
265
- r = {}
266
- record.each_pair do |k,v|
267
- r[k.to_sym] = v
268
- end
269
- active_resources << new(r, [], context, true)
270
- end
271
- unless is_collection
272
- return active_resources[0]
273
- end
274
- return active_resources
275
- end
276
-
277
- end
278
-
279
- self.name = "OpenObjectResource"
280
-
281
-
282
- # ******************** instance methods ********************
283
-
284
- attr_accessor :associations, :loaded_associations, :ir_model_data_id, :object_session
285
-
286
- def object_db; object_session[:database] || self.class.database || self.class.ooor.config[:database]; end
287
- def object_uid; object_session[:user_id] || self.class.user_id || self.class.ooor.config[:user_id]; end
288
- def object_pass; object_session[:password] || self.class.password || self.class.ooor.config[:password]; end
289
-
290
- # Ruby 1.9.compat, See also http://tenderlovemaking.com/2011/06/28/til-its-ok-to-return-nil-from-to_ary/
291
- def to_ary # :nodoc:
292
- nil
293
- end
294
-
295
- #try to wrap the object context inside the query.
296
- def rpc_execute(method, *args)
297
- if args[-1].is_a? Hash
298
- args[-1] = self.class.ooor.global_context.merge(object_session[:context]).merge(args[-1])
299
- elsif args.is_a?(Array)
300
- args += [self.class.ooor.global_context.merge(object_session[:context])]
301
- end
302
- self.class.rpc_execute_with_all(object_db, object_uid, object_pass, self.class.openerp_model, method, *args)
303
- end
304
-
305
- def reload_from_record!(record) load(record.attributes.merge(record.associations)) end
306
-
307
- def load(attributes, remove_root=false)#an attribute might actually be a association too, will be determined here
308
- self.class.reload_fields_definition(false, object_session)
309
- raise ArgumentError, "expected an attributes Hash, got #{attributes.inspect}" unless attributes.is_a?(Hash)
310
- @prefix_options, attributes = split_options(attributes)
311
- @associations ||= {}
312
- @attributes ||= {}
313
- @loaded_associations = {}
314
- attributes.each do |key, value|
315
- skey = key.to_s
316
- if self.class.associations_keys.index(skey) || value.is_a?(Array) #FIXME may miss m2o with inherits!
317
- @associations[skey] = value #the association because we want the method to load the association through method missing
318
- else
319
- @attributes[skey] = (value == false)? nil : value
320
- end
321
- end
322
- self
323
- end
324
-
325
- def available_fields
326
- msg = "\n*** AVAILABLE FIELDS ON #{self.class.name} ARE: ***"
327
- msg << "\n\n" << self.class.fields.sort {|a,b| a[1]['type']<=>b[1]['type']}.map {|i| "#{i[1]['type']} --- #{i[0]}"}.join("\n")
328
- %w[many2one one2many many2many polymorphic_m2o].each do |kind|
329
- msg << "\n\n"
330
- msg << (self.class.send "#{kind}_associations").map {|k, v| "{kind} --- #{v['relation']} --- #{k}"}.join("\n")
331
- end
332
- msg
333
- end
334
-
335
- #takes care of reading OpenERP default field values.
336
- def initialize(attributes = {}, default_get_list=false, context={}, persisted=false)
337
- @attributes = {}
338
- @prefix_options = {}
339
- @ir_model_data_id = attributes.delete(:ir_model_data_id)
340
- @object_session = {}
341
- @object_session[:user_id] = context.delete :user_id
342
- @object_session[:database] = context.delete :database
343
- @object_session[:password] = context.delete :password
344
- @object_session[:context] = context
345
- @persisted = persisted #TODO match 3.1 ActiveResource API
346
- if default_get_list == []
347
- load(attributes)
348
- else
349
- self.class.reload_fields_definition(false, object_session)
350
- attributes = rpc_execute("default_get", default_get_list || self.class.fields.keys + self.class.associations_keys, object_session[:context]).symbolize_keys!.merge(attributes.symbolize_keys!)
351
- load(attributes)
352
- end
353
- end
354
-
355
- def save(context={}, reload=true)
356
- new? ? create(context, reload) : update(context, reload)
357
- end
358
-
359
- #compatible with the Rails way but also supports OpenERP context
360
- def create(context={}, reload=true)
361
- self.id = rpc_execute('create', to_openerp_hash!, context)
362
- IrModelData.create(:model => self.class.openerp_model, :module => @ir_model_data_id[0], :name=> @ir_model_data_id[1], :res_id => self.id) if @ir_model_data_id
363
- reload_from_record!(self.class.find(self.id, :context => context)) if reload
364
- @persisted = true
365
- end
366
-
367
- #compatible with the Rails way but also supports OpenERP context
368
- def update(context={}, reload=true)
369
- rpc_execute('write', [self.id], to_openerp_hash!, context)
370
- reload_fields(context) if reload
371
- @persisted = true
372
- end
373
-
374
-
375
- #compatible with the Rails way but also supports OpenERP context
376
- def destroy(context={})
377
- rpc_execute('unlink', [self.id], context)
378
- end
379
-
380
- #OpenERP copy method, load persisted copied Object
381
- def copy(defaults={}, context={})
382
- self.class.find(rpc_execute('copy', self.id, defaults, context), :context => context)
383
- end
384
-
385
- #Generic OpenERP rpc method call
386
- def call(method, *args) rpc_execute(method, *args) end
387
-
388
- #Generic OpenERP on_change method
389
- def on_change(on_change_method, field_name, field_value, *args)
390
- result = self.class.rpc_execute_with_all(object_db, object_uid, object_pass, self.class.openerp_model, on_change_method, self.id && [id] || [], *args) #OpenERP doesn't accept context systematically in on_change events unfortunately
391
- if result["warning"]
392
- self.class.logger.info result["warning"]["title"]
393
- self.class.logger.info result["warning"]["message"]
394
- end
395
- load(@attributes.merge({field_name => field_value}).merge(result["value"]))
396
- end
397
-
398
- #wrapper for OpenERP exec_workflow Business Process Management engine
399
- def wkf_action(action, context={}, reload=true)
400
- self.class.rpc_exec_workflow_with_all(object_db, object_uid, object_pass, self.class.openerp_model, action, self.id) #FIXME looks like OpenERP exec_workflow doesn't accept context but it might be a bug
401
- reload_fields(context) if reload
402
- end
403
-
404
- def old_wizard_step(wizard_name, step='init', wizard_id=nil, form={}, context={})
405
- result = self.class.old_wizard_step(wizard_name, [self.id], step, wizard_id, form, {})
406
- FormModel.new(wizard_name, result[0], nil, nil, result[1], [self], self.class.ooor.global_context)
407
- end
408
-
409
- def log(message, context={}) rpc_execute('log', id, message, context) end
410
-
411
- def type() method_missing(:type) end #skips deprecated Object#type method
412
-
413
- # fakes associations like much like ActiveRecord according to the cached OpenERP data model
414
- def relationnal_result(method_name, *arguments)
415
- self.class.reload_fields_definition(false, object_session)
416
- if self.class.many2one_associations.has_key?(method_name)
417
- return false unless @associations[method_name]
418
- load_association(self.class.many2one_associations[method_name]['relation'], @associations[method_name].is_a?(Integer) && @associations[method_name] || @associations[method_name][0], *arguments)
419
- elsif self.class.one2many_associations.has_key?(method_name)
420
- load_association(self.class.one2many_associations[method_name]['relation'], @associations[method_name], *arguments) || []
421
- elsif self.class.many2many_associations.has_key?(method_name)
422
- load_association(self.class.many2many_associations[method_name]['relation'], @associations[method_name], *arguments) || []
423
- elsif self.class.polymorphic_m2o_associations.has_key?(method_name)
424
- values = @associations[method_name].split(',')
425
- load_association(values[0], values[1].to_i, *arguments)
426
- else
427
- false
428
- end
429
- end
430
-
431
- def method_missing(method_symbol, *arguments)
432
- method_name = method_symbol.to_s
433
- is_assign = method_name.end_with?('=')
434
- method_key = method_name.sub('=', '')
435
- self.class.reload_fields_definition(false, object_session)
436
-
437
- if attributes.has_key?(method_key)
438
- return super
439
- elsif @loaded_associations.has_key?(method_name)
440
- @loaded_associations[method_name]
441
- elsif @associations.has_key?(method_name)
442
- result = relationnal_result(method_name, *arguments)
443
- @loaded_associations[method_name] = result and return result if result
444
- elsif is_assign
445
- known_associations = self.class.associations_keys + self.class.many2one_associations.collect {|k, field| self.class.const_get(field['relation'], object_session).associations_keys}.flatten
446
- if known_associations.index(method_key)
447
- @associations[method_key] = arguments[0]
448
- @loaded_associations[method_key] = arguments[0]
449
- return
450
- end
451
- know_fields = self.class.fields.keys + self.class.many2one_associations.collect {|k, field| self.class.const_get(field['relation'], object_session).fields.keys}.flatten
452
- @attributes[method_key] = arguments[0] and return if know_fields.index(method_key)
453
- elsif self.class.fields.has_key?(method_key) || self.class.associations_keys.index(method_name) #unloaded field/association
454
- load(rpc_execute('read', [id], [method_key], *arguments || object_session)[0] || {})
455
- return method_missing(method_key, *arguments)
456
- elsif id #it's an action
457
- arguments += [{}] unless arguments.last.is_a?(Hash)
458
- rpc_execute(method_key, [id], *arguments) #we assume that's an action
459
- else
460
- super
461
- end
462
-
463
- rescue RuntimeError => e
464
- e.message << "\n" + available_fields if e.message.index("AttributeError")
465
- raise e
466
- end
467
-
468
- #Add get_report_data to obtain [report["result"],report["format]] of a concrete openERP Object
469
- def get_report_data(report_name, report_type="pdf", context={})
470
- self.class.get_report_data(report_name, [self.id], report_type, context)
471
- end
472
-
473
- private
474
-
475
- def load_association(model_key, ids, *arguments)
476
- options = arguments.extract_options!
477
- related_class = self.class.const_get(model_key, object_session)
478
- related_class.send :find, ids, :fields => options[:fields] || options[:only] || [], :context => options[:context] || object_session[:context]
479
- end
480
-
481
- def reload_fields(context)
482
- records = self.class.find(self.id, :context => context, :fields => @attributes.keys + @associations.keys)
483
- reload_from_record!(records)
484
- end
485
- end
486
- end