ooor 1.1.0 → 1.2.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.
data/README.md CHANGED
@@ -76,7 +76,7 @@ OOOR also extends ActiveResource a bit with special request parameters (like :do
76
76
  Installation
77
77
  ------------
78
78
 
79
- You can use OOOR in a standalone (J)Ruby application, or in a Rails application.
79
+ You can use OOOR in a standalone (J)Ruby application, or in a Rails application, it only depends on the activeresource gem.
80
80
  For both example we assume that you already started some OpenERP server on localhost, with XML/RPC on port 8069 (default),
81
81
  with a database called 'mybase', with username 'admin' and password 'admin'.
82
82
 
@@ -91,8 +91,7 @@ In all case, you first need to install the ooor gem:
91
91
  Let's test OOOR in an irb console (irb command):
92
92
  $ require 'rubygems'
93
93
  $ require 'ooor'
94
- $ include Ooor
95
- $ Ooor.reload!({:url => 'http://localhost:8069/xmlrpc', :database => 'mybase', :username => 'admin', :password => 'admin'})
94
+ $ Ooor.new({:url => 'http://localhost:8069/xmlrpc', :database => 'mybase', :username => 'admin', :password => 'admin'})
96
95
  This should load all your OpenERP models into Ruby proxy Activeresource objects. Of course there are option to load only some models.
97
96
  Let's try to retrieve the user with id 1:
98
97
  $ ResUsers.find(1)
@@ -116,13 +115,11 @@ You can then use all the OOOR API upon all loaded OpenERP models in your regular
116
115
  A good way to start playing with OOOR is inside the console, using:
117
116
  $ ruby script/console #or jruby script/console on JRuby of course
118
117
 
119
- Enabling REST HTTP routes to your OpenERP models:
120
- in your config/route.rb, you can alternatively enable routes to all your OpenERP models by addding:
121
- $ OpenObjectsController.load_all_controllers(map)
122
-
123
- Or only enable the route to some specific model instead (here partners):
124
- $ map.resources :res_partner
118
+ Note: when boostraping Ooor in a Rails application, the default Ooor instance is stored in the OOOR constant.
119
+ So for instance you can know all loaded models doing OOOR.all_loaded_models; this is used by [OooREST](http://github.com/rvalyi/ooorest) to register all the REST controllers.
125
120
 
121
+ Enabling REST HTTP routes to your OpenERP models:
122
+ The REST Controller layer of OOOR has been moved as a thin separate gem called [OooREST](http://github.com/rvalyi/ooorest).
126
123
 
127
124
 
128
125
  API usage
@@ -140,15 +137,15 @@ Basic finders:
140
137
  $ ProductProduct.find(:last)
141
138
 
142
139
 
143
- OpenERP domain support:
140
+ OpenERP domain support (same as OpenERP):
144
141
 
145
142
  $ ResPartner.find(:all, :domain=>[['supplier', '=', 1],['active','=',1]])
146
- More subbtle now, remember OpenERP use a kind of inverse polish notation for complex domains,
143
+ More subtle now, remember OpenERP use a kind of inverse polish notation for complex domains,
147
144
  here we look for a product in category 1 AND which name is either 'PC1' OR 'PC2':
148
145
  $ ProductProduct.find(:all, :domain=>[['categ_id','=',1],'|',['name', '=', 'PC1'],['name','=','PC2']])
149
146
 
150
147
 
151
- OpenERP context support:
148
+ OpenERP context support (same as OpenERP):
152
149
 
153
150
  $ ProductProduct.find(1, :context => {:my_key => 'value'})
154
151
 
@@ -166,10 +163,11 @@ Arguments are: domain, offset=0, limit=false, order=false, context={}, count=fal
166
163
 
167
164
  Relations (many2one, one2many, many2many) support:
168
165
 
169
- $ SaleOrder.find(1).order_line
166
+ $ SaleOrder.find(1).order_line #one2many relation
170
167
  $ p = ProductProduct.find(1)
171
168
  $ p.product_tmpl_id #many2one relation
172
- $ p.product_tmpl_id.taxes_id = [6, 0, [1,2]] #create many2many associations,
169
+ $ p.taxes_id #automagically reads man2many relation inherited via the product_tmpl_id inheritance relation
170
+ $ p.taxes_id = [1,2] #save a many2many relation, notice how we bypass the awkward OpenERP syntax for many2many (would require [6,0, [1,2]]) ,
173
171
  $ p.save #assigns taxes with id 1 and 2 as sale taxes,
174
172
  see [the official OpenERP documentation](http://doc.openerp.com/developer/5_18_upgrading_server/19_1_upgrading_server.html?highlight=many2many)
175
173
 
@@ -180,9 +178,6 @@ Inherited relations support:
180
178
 
181
179
  Please notice that loaded relations are cached (to avoid hitting OpenERP over and over)
182
180
  until the root object is reloaded (after save/update for instance).
183
- Currently, save/update doesn't save the whole object graph but only the current object.
184
- We might change this in the future to match the way OpenERP clients are working which
185
- is supported by the OpenERP ORM, see issue: http://github.com/rvalyi/ooor/issues/#issue/3
186
181
 
187
182
 
188
183
  Load only specific fields support (faster than loading all fields):
@@ -229,26 +224,48 @@ Call workflow:
229
224
 
230
225
  On Change methods:
231
226
 
232
- $ l=SaleOrderLine.new
233
- $ l.on_change('product_id_change', 1, 1, false, false, false, false, false, false, 1)
234
- $ => #<Ooor::SaleOrderLine:0x10c2cfe1 @prefix_options={}, @attributes={"product_packaging"=>false, "product_uos_qty"=>false, "th_weight"=>0}>
227
+ Note: currently OOOR doesn't deal with the View layer, or has a very limited support for forms for the wizards.
228
+ So, it's not possible so far for OOOR to know an on_change signature. Because of this, the on_change syntax is bit awkward
229
+ as you will see: you need to explicitely tell the on_change name, the parameter name that changed, the new value and finally
230
+ enfore the on_change syntax (looking at the OpenERP model code or view or XML/RPC logs will help you to find out). But
231
+ ultimately it works:
232
+
233
+ $ l = SaleOrderLine.new
234
+ $ l.on_change('product_id_change', :product_id, 20, 1, 20, 1, false, 1, false, false, 7, 'en_US', true, false, false, false)
235
+ $ => #<SaleOrderLine:0x7f76118b4348 @prefix_options={}, @relations={"product_uos"=>false, "product_id"=>20, "product_uom"=>1, "tax_id"=>[]}, @loaded_relations={}, @attributes={"name"=>"[TOW1] ATX Mid-size Tower", "product_uos_qty"=>1, "delay"=>1.0, "price_unit"=>37.5, "type"=>"make_to_stock", "th_weight"=>0}>
235
236
  Notice that it reloads the Objects attrs and print warning message accordingly
236
237
 
237
238
 
239
+ On the fly one2many object graph update/creation:
240
+
241
+ Just like the OpenERP GTK client (and unlike the web client), in OOOR you can pass create/update
242
+ one2many relation in place directly. For instance:
243
+ $ so = SaleOrder.new
244
+ $ so.on_change('onchange_partner_id', :partner_id, 1, 1, false) #auto-complete the address and other data based on the partner
245
+ $ so.order_line = [SaleOrderLine.new(:name => 'sl1', :product_id => 1, :price_unit => 42, :product_uom => 1)] #create one order line
246
+ $ so.save
247
+ $ so.amount_total
248
+ $ => 42.0
249
+
250
+
238
251
  Call aribtrary method:
239
252
 
240
253
  $ use static ObjectClass.rpc_execute_with_all method
241
254
  $ or object.call(method_name, args*) #were args is an aribtrary list of arguments
242
- $ or use the method missing wrapper that will proxy any OpenERP osv.py/orm.py method, see fo instance:
255
+
256
+ Class methods from are osv.py/orm.py proxied to OpenERP directly (as the web client does):
243
257
  $ ResPartner.name_search('ax', [], 'ilike', {})
244
258
  $ ProductProduct.fields_view_get(132, 'tree', {})
245
259
 
246
260
 
247
- Call old wizards:
261
+ Call old style wizards:
248
262
 
249
263
  $ inv = AccountInvoice.find(4)
264
+ $ #in case the inv.state is 'draft', do inv.wkf_action('invoice_open')
250
265
  $ wizard = inv.old_wizard_step('account.invoice.pay') #tip: you can inspect the wizard fields, arch and datas
251
266
  $ wizard.reconcile({:journal_id => 6, :name =>"from_rails"}) #if you want to pay all; will give you a reloaded invoice
267
+ $ inv.state
268
+ $ => "paid"
252
269
  $ #or if you want a payment with a write off:
253
270
  $ wizard.writeoff_check({"amount" => 12, "journal_id" => 6, "name" =>'from_rails'}) #use the button name as the wizard method
254
271
  $ wizard.reconcile({required missing write off fields...}) #will give you a reloaded invoice because state is 'end'
@@ -274,21 +291,7 @@ However you might want to change that. 2 solutions:
274
291
  $ In the config yaml file or hash, set the :log_level parameter
275
292
 
276
293
 
277
-
278
- REST HTTP API:
279
-
280
- $ http://localhost:3000/res_partner
281
- $ http://localhost:3000/res_partner.xml
282
- $ http://localhost:3000/res_partner.json
283
- $ http://localhost:3000/res_partner/2
284
- $ http://localhost:3000/res_partner/2.json
285
- $ http://localhost:3000/res_partner/2.xml
286
- $ http://localhost:3000/res_partner/[2,3,4].xml
287
- $ http://localhost:3000/res_partner/[2,3,4].json
288
-
289
- $ TODO http://localhost:3000/res_partner.xml?active=1
290
-
291
- $ TODO http://localhost:3000/res_partner.xml?domain=TODO
294
+ [Drawing OpenERP UML diagrams with OOOR](http://wiki.github.com/rvalyi/ooor/drawing-openerp-uml-diagrams-with-ooor)
292
295
 
293
296
 
294
297
  FAQ
@@ -302,17 +305,16 @@ Then create indents in the log before doing some action and watch your logs care
302
305
 
303
306
  ### How can I load/reload my OpenERP models into my Ruby application?
304
307
 
305
- You can load/reload your models at any time (even in console), using the Ooor.reload! method, for instance:
306
- $ Ooor.reload!({:url => 'http://localhost:8069/xmlrpc', :database => 'mybase', :username => 'admin', :password => 'admin'})
308
+ You can load/reload your models at any time (even in console), creating a new Ooor instance that will override the class definitions:
309
+ $ Ooor.new({:url => 'http://localhost:8069/xmlrpc', :database => 'mybase', :username => 'admin', :password => 'admin'})
307
310
  or using a config YAML file instead:
308
- $ Ooor.reload!("config/ooor.yml")
311
+ $ Ooor.new("config/ooor.yml")
309
312
 
310
313
  ### Do I need to load all the OpenERP models in my Ruby application?
311
314
 
312
315
  You can load only some OpenERP models (not all), which is faster and better in term of memory/security:
313
316
  $ Ooor.reload!({:models => [res.partner, product.template, product.product], :url => 'http://localhost:8069/xmlrpc', :database => 'mybase', :username => 'admin', :password => 'admin'})
314
317
 
315
-
316
318
  ### Isn't OOOR slow?
317
319
 
318
320
  You might think that proxying a Python based server through a Rails app using XML/RPC would be dog slow. Well, not too much actually.
@@ -338,18 +340,4 @@ In you app/model directory, create a product_product.rb file with inside, the re
338
340
  $ end
339
341
  $ end
340
342
 
341
- Now a ProductProduct resource got a method foo, returning "bar".
342
-
343
- ### Can I extend the OOOR controllers?
344
-
345
- Yes, you can do that, see the "how to extends models" section as it's very similar. Instead, in the app/controllers directory, you'll re-open defined controllers,
346
- for the product.product controllers, it means creating a product_product_controller.rb file with:
347
-
348
- $ class ProductProduct < OpenObjectsController
349
- $ def foo
350
- $ render :text => "bar"
351
- $ end
352
- $ end
353
-
354
- Now, if you register that new method in your route.rb file GET /product_product/1/foo will render "bar" on your browser screen.
355
- You could instead just customize the existing CRUD methods so you don't need to regiter any other route in route.rb.
343
+ Now a ProductProduct resource got a method foo, returning "bar".
@@ -1,30 +1,29 @@
1
1
  require 'xmlrpc/client'
2
- require 'activeresource'
2
+ require 'active_resource'
3
3
  require 'app/models/open_object_ui'
4
+ require 'app/models/uml'
4
5
 
5
6
  #TODO implement passing session credentials to RPC methods (concurrent access of different user credentials in Rails)
6
7
 
7
8
  class OpenObjectResource < ActiveResource::Base
9
+ include UML
8
10
 
9
11
  # ******************** class methods ********************
10
12
  class << self
11
13
 
12
14
  cattr_accessor :logger
13
15
  attr_accessor :openerp_id, :info, :access_ids, :name, :openerp_model, :field_ids, :state, #model class attributes assotiated to the OpenERP ir.model
14
- :field_defined, :many2one_relations, :one2many_relations, :many2many_relations,
15
- :openerp_database, :user_id
16
+ :fields, :fields_defined, :many2one_relations, :one2many_relations, :many2many_relations,
17
+ :openerp_database, :user_id, :scope_prefix, :ooor
16
18
 
17
- def class_name_from_model_key(model_key)
18
- model_key.split('.').collect {|name_part| name_part[0..0].upcase + name_part[1..-1]}.join
19
+ def class_name_from_model_key(model_key=self.openerp_model)
20
+ self.scope_prefix + model_key.split('.').collect {|name_part| name_part.capitalize}.join
19
21
  end
20
22
 
21
23
  def reload_fields_definition(force = false)
22
- if self != IrModel and self != IrModelFields and (force or not @field_defined)#TODO have a way to force reloading @field_ids too eventually
23
- fields = IrModelFields.find(@field_ids)
24
+ if not (self.to_s.match('IrModel') || self.to_s.match('IrModelFields')) and (force or not @fields_defined)#TODO have a way to force reloading @field_ids too eventually
25
+ fields = Object.const_get(self.scope_prefix + 'IrModelFields').find(@field_ids)
24
26
  @fields = {}
25
- @many2one_relations = {}
26
- @one2many_relations = {}
27
- @many2many_relations = {}
28
27
  fields.each do |field|
29
28
  case field.attributes['ttype']
30
29
  when 'many2one'
@@ -37,38 +36,11 @@ class OpenObjectResource < ActiveResource::Base
37
36
  @fields[field.attributes['name']] = field
38
37
  end
39
38
  end
40
- logger.info "#{fields.size} fields loaded"
39
+ logger.info "#{fields.size} fields loaded in model #{self.class}"
41
40
  end
42
- @field_defined = true
41
+ @fields_defined = true
43
42
  end
44
43
 
45
- def define_openerp_model(arg, url, database, user_id, pass, binding)
46
- param = (arg.is_a? OpenObjectResource) ? arg.attributes.merge(arg.relations) : {'model' => arg}
47
- model_key = param['model']
48
- Ooor.all_loaded_models.push(model_key)
49
- model_class_name = class_name_from_model_key(model_key)
50
- logger.info "registering #{model_class_name} as a Rails ActiveResource Model wrapper for OpenObject #{model_key} model"
51
- definition = "
52
- class #{model_class_name} < OpenObjectResource
53
- self.site = '#{url || Ooor.base_url}'
54
- self.user = #{user_id}
55
- self.password = #{pass || false}
56
- self.openerp_database = '#{database}'
57
- self.openerp_model = '#{model_key}'
58
- self.openerp_id = #{param['id'] || false}
59
- self.info = '#{(param['info'] || '').gsub("'",' ')}'
60
- self.name = '#{param['name']}'
61
- self.state = '#{param['state']}'
62
- self.field_ids = #{(param['field_id'] and '[' + param['field_id'].join(',') + ']') || false}
63
- self.access_ids = #{(param['access_ids'] and '[' + param['access_ids'].join(',') + ']') || false}
64
- self.many2one_relations = {}
65
- self.one2many_relations = {}
66
- self.many2many_relations = {}
67
- end"
68
- eval definition, binding
69
- end
70
-
71
-
72
44
  # ******************** remote communication ********************
73
45
 
74
46
  #OpenERP search method
@@ -78,12 +50,7 @@ class OpenObjectResource < ActiveResource::Base
78
50
 
79
51
  def client(url)
80
52
  @clients ||= {}
81
- @client = @clients[url]
82
- unless @clientl
83
- @client ||= XMLRPC::Client.new2(url)
84
- @clients[url] = @client
85
- end
86
- return @client
53
+ @clients[url] ||= XMLRPC::Client.new2(url)
87
54
  end
88
55
 
89
56
  #corresponding method for OpenERP osv.execute(self, db, uid, obj, method, *args, **kw) method
@@ -92,16 +59,16 @@ class OpenObjectResource < ActiveResource::Base
92
59
  end
93
60
 
94
61
  def rpc_execute_with_object(object, method, *args)
95
- rpc_execute_with_all(@database || Ooor.config[:database], @user_id || Ooor.config[:user_id], @password || Ooor.config[:password], object, method, *args)
62
+ rpc_execute_with_all(@database || @ooor.config[:database], @user_id || @ooor.config[:user_id], @password || @ooor.config[:password], object, method, *args)
96
63
  end
97
64
 
98
65
  #corresponding method for OpenERP osv.execute(self, db, uid, obj, method, *args, **kw) method
99
66
  def rpc_execute_with_all(db, uid, pass, obj, method, *args)
100
67
  if args[-1].is_a? Hash
101
- args[-1] = Ooor.global_context.merge(args[-1])
68
+ args[-1] = @ooor.global_context.merge(args[-1])
102
69
  end
103
70
  logger.debug "rpc_execute_with_all: rpc_methods: 'execute', db: #{db.inspect}, uid: #{uid.inspect}, pass: #{pass.inspect}, obj: #{obj.inspect}, method: #{method}, *args: #{args.inspect}"
104
- try_with_pretty_error_log { client((@database && @site || Ooor.base_url) + "/object").call("execute", db, uid, pass, obj, method, *args) }
71
+ try_with_pretty_error_log { client((@database && @site || @ooor.base_url) + "/object").call("execute", db, uid, pass, obj, method, *args) }
105
72
  end
106
73
 
107
74
  #corresponding method for OpenERP osv.exec_workflow(self, db, uid, obj, method, *args)
@@ -110,23 +77,23 @@ class OpenObjectResource < ActiveResource::Base
110
77
  end
111
78
 
112
79
  def rpc_exec_workflow_with_object(object, action, *args)
113
- rpc_exec_workflow_with_all(@database || Ooor.config[:database], @user_id || Ooor.config[:user_id], @password || Ooor.config[:password], object, action, *args)
80
+ rpc_exec_workflow_with_all(@database || @ooor.config[:database], @user_id || @ooor.config[:user_id], @password || @ooor.config[:password], object, action, *args)
114
81
  end
115
82
 
116
83
  def rpc_exec_workflow_with_all(db, uid, pass, obj, action, *args)
117
84
  if args[-1].is_a? Hash
118
- args[-1] = Ooor.global_context.merge(args[-1])
85
+ args[-1] = @ooor.global_context.merge(args[-1])
119
86
  end
120
87
  logger.debug "rpc_execute_with_all: rpc_methods: 'exec_workflow', db: #{db.inspect}, uid: #{uid.inspect}, pass: #{pass.inspect}, obj: #{obj.inspect}, action #{action}, *args: #{args.inspect}"
121
- try_with_pretty_error_log { client((@database && @site || Ooor.base_url) + "/object").call("exec_workflow", db, uid, pass, obj, action, *args) }
88
+ try_with_pretty_error_log { client((@database && @site || @ooor.base_url) + "/object").call("exec_workflow", db, uid, pass, obj, action, *args) }
122
89
  end
123
90
 
124
91
  def old_wizard_step(wizard_name, ids, step='init', wizard_id=nil, form={}, context={}, report_type='pdf')
125
- context = Ooor.global_context.merge(context)
92
+ context = @ooor.global_context.merge(context)
126
93
  unless wizard_id
127
- wizard_id = try_with_pretty_error_log { 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) }
94
+ wizard_id = try_with_pretty_error_log { 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) }
128
95
  end
129
- [wizard_id, try_with_pretty_error_log { 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, {'model' => @openerp_model, 'form' => form, 'id' => ids[0], 'report_type' => report_type, 'ids' => ids}, step, context) }]
96
+ [wizard_id, try_with_pretty_error_log { 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, {'model' => @openerp_model, 'form' => form, 'id' => ids[0], 'report_type' => report_type, 'ids' => ids}, step, context) }]
130
97
  end
131
98
 
132
99
  #grab the eventual error log from OpenERP response as OpenERP doesn't enforce carefuly
@@ -146,17 +113,13 @@ class OpenObjectResource < ActiveResource::Base
146
113
  raise
147
114
  end
148
115
 
149
- def method_missing(method_symbol, *arguments)
150
- return self.rpc_execute(method_symbol.to_s, *arguments)
151
- end
116
+ def method_missing(method_symbol, *arguments) return self.rpc_execute(method_symbol.to_s, *arguments) end
152
117
 
153
- def load_relation(model_key, ids, *arguments)
118
+ def load_relation(model_key, ids, scope_prefix, *arguments)
154
119
  options = arguments.extract_options!
155
- unless Ooor.all_loaded_models.index(model_key)
156
- model = IrModel.find(:first, :domain => [['model', '=', model_key]])
157
- define_openerp_model(model, nil, nil, nil, nil, Ooor.binding)
158
- end
159
- relation_model_class = eval class_name_from_model_key(model_key)
120
+ class_name = class_name_from_model_key(model_key)
121
+ @ooor.define_openerp_model(model_key, nil, nil, nil, nil, scope_prefix) unless Object.const_defined?(class_name)
122
+ relation_model_class = Object.const_get(class_name)
160
123
  relation_model_class.send :find, ids, :fields => options[:fields] || [], :context => options[:context] || {}
161
124
  end
162
125
 
@@ -180,9 +143,7 @@ class OpenObjectResource < ActiveResource::Base
180
143
  end
181
144
 
182
145
  #TODO, make sense?
183
- def find_one
184
- raise "Not implemented yet, go on!"
185
- end
146
+ def find_one; raise"Not implemented yet, go on!"; end
186
147
 
187
148
  # Find a single resource from the default URL
188
149
  def find_single(scope, options)
@@ -216,28 +177,87 @@ class OpenObjectResource < ActiveResource::Base
216
177
 
217
178
  attr_accessor :relations, :loaded_relations
218
179
 
219
- def pre_cast_attributes
220
- @attributes.each {|k, v| @attributes[k] = ((v.is_a? BigDecimal) ? Float(v) : v)}
180
+ def cast_attributes_to_ruby!
181
+ @attributes.each do |k, v|
182
+ if self.class.fields[k]
183
+ if v.is_a?(String)
184
+ case self.class.fields[k].ttype
185
+ when 'datetime'
186
+ @attributes[k] = Time.parse(v)
187
+ when 'date'
188
+ @attributes[k] = Date.parse(v)
189
+ end
190
+ end
191
+ end
192
+ end
221
193
  end
222
194
 
223
- def reload_from_record!(record)
224
- load(record.attributes, record.relations)
195
+ def cast_attributes_to_openerp!
196
+ @attributes.each do |k, v|
197
+ @attributes[k] = ((v.is_a? BigDecimal) ? Float(v) : v)
198
+ if self.class.fields[k]
199
+ case self.class.fields[k].ttype
200
+ when 'datetime'
201
+ @attributes[k] = "#{v.year}-#{v.month}-#{v.day} #{v.hour}:#{v.min}:#{v.sec}" if v.respond_to?(:sec)
202
+ when 'date'
203
+ @attributes[k] = "#{v.year}-#{v.month}-#{v.day}" if v.is_a?(Date)
204
+ end
205
+ end
206
+ end
207
+ end
208
+
209
+ def cast_relations_to_openerp!
210
+ @relations.reject! do |k, v| #reject non asigned many2one or empty list
211
+ v.is_a?(Array) && (v.size == 0 or v[1].is_a?(String))
212
+ end
213
+
214
+ def cast_relation(k, v, one2many_relations, many2many_relations)
215
+ if one2many_relations[k]
216
+ return v.collect! do |value|
217
+ if value.is_a?(OpenObjectResource) #on the fly creation as in the GTK client
218
+ [0, 0, value.to_openerp_hash!]
219
+ else
220
+ [1, value, {}]
221
+ end
222
+ end
223
+ elsif many2many_relations[k]
224
+ return v = [[6, 0, v]]
225
+ end
226
+ end
227
+
228
+ @relations.each do |k, v| #see OpenERP awkward relations API
229
+ new_rel = self.cast_relation(k, v, self.class.one2many_relations, self.class.many2many_relations)
230
+ if new_rel #matches a known o2m or m2m
231
+ @relations[k] = new_rel
232
+ else
233
+ self.class.many2one_relations.each do |k2, field| #try to cast the relation to na inherited o2m or m2m:
234
+ class_name = self.class.class_name_from_model_key(field.relation)
235
+ self.class.ooor.define_openerp_model(field.relation, nil, nil, nil, nil, self.class.scope_prefix) unless Object.const_defined?(class_name)
236
+ linked_class = Object.const_get(class_name)
237
+ linked_class.reload_fields_definition unless linked_class.fields_defined
238
+ new_rel = self.cast_relation(k, v, linked_class.one2many_relations, linked_class.many2many_relations)
239
+ @relations[k] = new_rel and break if new_rel
240
+ end
241
+ end
242
+ end
225
243
  end
226
244
 
245
+ def reload_from_record!(record) load(record.attributes, record.relations) end
246
+
227
247
  def load(attributes, relations={})
228
- self.class.reload_fields_definition unless self.class.field_defined
248
+ self.class.reload_fields_definition() unless self.class.fields_defined
229
249
  raise ArgumentError, "expected an attributes Hash, got #{attributes.inspect}" unless attributes.is_a?(Hash)
230
250
  @prefix_options, attributes = split_options(attributes)
231
251
  @relations = relations
252
+ @attributes = {}
232
253
  @loaded_relations = {}
233
254
  attributes.each do |key, value|
234
255
  skey = key.to_s
235
- if self.class.many2one_relations.has_key?(skey) || self.class.one2many_relations.has_key?(skey) || self.class.many2many_relations.has_key?(skey)
256
+ if self.class.many2one_relations.has_key?(skey) || self.class.one2many_relations.has_key?(skey) ||
257
+ self.class.many2many_relations.has_key?(skey) || value.is_a?(Array)
236
258
  relations[skey] = value #the relation because we want the method to load the association through method missing
237
259
  else
238
260
  case value
239
- when self.class.many2one_relations.has_key?(skey) || self.class.one2many_relations.has_key?(skey) || self.class.many2many_relations.has_key?(skey)
240
- relations[skey] = value #the relation because we want the method to load the association through method missing
241
261
  when Hash
242
262
  resource = find_or_create_resource_for(key) #TODO check!
243
263
  @attributes[skey] = resource@attributes[skey].new(value)
@@ -246,25 +266,40 @@ class OpenObjectResource < ActiveResource::Base
246
266
  end
247
267
  end
248
268
  end
249
-
269
+ cast_attributes_to_ruby!
250
270
  self
251
271
  end
252
272
 
253
- #compatible with the Rails way but also supports OpenERP context; TODO: properly pass one2many and many2many object graph like GTK client
273
+ def display_available_fields
274
+ self.class.logger.debug ""
275
+ self.class.logger.debug "*** DIRECTLY AVAILABLE FIELDS ON OBJECT #{self} ARE: ***\n"
276
+ self.class.fields.sort {|a,b| a[1].ttype<=>b[1].ttype}.each {|i| self.class.logger.debug "#{i[1].ttype} --- #{i[0]}"}
277
+ self.class.logger.debug ""
278
+ self.class.many2one_relations.each {|k, v| self.class.logger.debug "many2one --- #{v.relation} --- #{k}"}
279
+ self.class.logger.debug ""
280
+ self.class.one2many_relations.each {|k, v| self.class.logger.debug "one2many --- #{v.relation} --- #{k}"}
281
+ self.class.logger.debug ""
282
+ self.class.many2many_relations.each {|k, v| self.class.logger.debug "many2many --- #{v.relation} --- #{k}"}
283
+ self.class.logger.debug ""
284
+ self.class.logger.debug "YOU CAN ALSO USE THE INHERITED FIELDS FROM THE INHERITANCE MANY2ONE RELATIONS OR THE OBJECT METHODS..."
285
+ self.class.logger.debug ""
286
+ end
287
+
288
+ def to_openerp_hash!
289
+ cast_attributes_to_openerp!
290
+ cast_relations_to_openerp!
291
+ @attributes.reject {|key, value| key == 'id'}.merge(@relations)
292
+ end
293
+
294
+ #compatible with the Rails way but also supports OpenERP context
254
295
  def create(context={})
255
- self.pre_cast_attributes
256
- m2o = @relations.reject{|k, v| !self.class.many2one_relations.has_key?(k)}
257
- vals = @attributes.merge(m2o.merge(m2o){|k, v| v.is_a?(Array) ? v[0] : v})
258
- self.id = self.class.rpc_execute('create', vals, context)
296
+ self.id = self.class.rpc_execute('create', to_openerp_hash!, context)
259
297
  reload_from_record!(self.class.find(self.id, :context => context))
260
298
  end
261
299
 
262
- #compatible with the Rails way but also supports OpenERP context; TODO: properly pass one2many and many2many object graph like GTK client
300
+ #compatible with the Rails way but also supports OpenERP context
263
301
  def update(context={})
264
- self.pre_cast_attributes
265
- m2o = @relations.reject{|k, v| !self.class.many2one_relations.has_key?(k)}
266
- vals = @attributes.reject {|key, value| key == 'id'}.merge(m2o.merge(m2o){|k, v| v.is_a?(Array) ? v[0] : v})
267
- self.class.rpc_execute('write', self.id, vals, context)
302
+ self.class.rpc_execute('write', self.id, to_openerp_hash!, context)
268
303
  reload_from_record!(self.class.find(self.id, :context => context))
269
304
  end
270
305
 
@@ -274,21 +309,21 @@ class OpenObjectResource < ActiveResource::Base
274
309
  end
275
310
 
276
311
  #OpenERP copy method, load persisted copied Object
277
- def copy(defaults=[], context={})
312
+ def copy(defaults={}, context={})
278
313
  self.class.find(self.class.rpc_execute('copy', self.id, defaults, context), :context => context)
279
314
  end
280
315
 
281
316
  #Generic OpenERP rpc method call
282
- def call(method, *args)
283
- self.class.rpc_execute(method, *args)
284
- end
317
+ def call(method, *args) self.class.rpc_execute(method, *args) end
285
318
 
286
319
  #Generic OpenERP on_change method
287
- def on_change(on_change_method, *args)
288
- result = self.class.rpc_execute(on_change_method, *args)
289
- self.classlogger.info result["warning"]["title"] if result["warning"]
290
- self.class.logger.info result["warning"]["message"] if result["warning"]
291
- load(result["value"])
320
+ def on_change(on_change_method, field_name, field_value, *args)
321
+ result = self.class.rpc_execute(on_change_method, self.id && [id] || [], *args)
322
+ if result["warning"]
323
+ self.class.logger.info result["warning"]["title"]
324
+ self.class.logger.info result["warning"]["message"]
325
+ end
326
+ load({field_name => field_value}.merge(result["value"]))
292
327
  end
293
328
 
294
329
  #wrapper for OpenERP exec_workflow Business Process Management engine
@@ -299,20 +334,20 @@ class OpenObjectResource < ActiveResource::Base
299
334
 
300
335
  def old_wizard_step(wizard_name, step='init', wizard_id=nil, form={}, context={})
301
336
  result = self.class.old_wizard_step(wizard_name, [self.id], step, wizard_id, form, {})
302
- OpenObjectWizard.new(wizard_name, result[0], result[1], [self])
337
+ OpenObjectWizard.new(wizard_name, result[0], result[1], [self], self.class.ooor.global_context)
303
338
  end
304
339
 
305
340
 
306
341
  # ******************** fake associations like much like ActiveRecord according to the cached OpenERP data model ********************
307
342
 
308
343
  def relationnal_result(method_name, *arguments)
309
- self.class.reload_fields_definition unless self.class.field_defined
344
+ self.class.reload_fields_definition unless self.class.fields_defined
310
345
  if self.class.many2one_relations.has_key?(method_name)
311
- self.class.load_relation(self.class.many2one_relations[method_name].relation, @relations[method_name][0], *arguments)
346
+ self.class.load_relation(self.class.many2one_relations[method_name].relation, @relations[method_name][0], self.class.scope_prefix, *arguments)
312
347
  elsif self.class.one2many_relations.has_key?(method_name)
313
- self.class.load_relation(self.class.one2many_relations[method_name].relation, @relations[method_name], *arguments)
348
+ self.class.load_relation(self.class.one2many_relations[method_name].relation, @relations[method_name], self.class.scope_prefix, *arguments)
314
349
  elsif self.class.many2many_relations.has_key?(method_name)
315
- self.class.load_relation(self.class.many2many_relations[method_name].relation, @relations[method_name], *arguments)
350
+ self.class.load_relation(self.class.many2many_relations[method_name].relation, @relations[method_name], self.class.scope_prefix, *arguments)
316
351
  else
317
352
  false
318
353
  end
@@ -320,24 +355,33 @@ class OpenObjectResource < ActiveResource::Base
320
355
 
321
356
  def method_missing(method_symbol, *arguments)
322
357
  method_name = method_symbol.to_s
358
+ return super if attributes.has_key?(method_name) or attributes.has_key?(method_name.first(-1))
359
+ if method_name.end_with?('=')
360
+ @relations[method_name.sub('=', '')] = *arguments
361
+ return
362
+ end
323
363
  return @loaded_relations[method_name] if @loaded_relations.has_key?(method_name)
324
- if @relations.has_key?(method_name) and !@relations[method_name]
325
- return false
326
- else
327
- result = relationnal_result(method_name, *arguments)
328
- if result
329
- @loaded_relations[method_name] = result
330
- return result
331
- elsif @relations and @relations.has_key?(method_name) and !self.class.many2one_relations.empty?
332
- #maybe the relation is inherited or could be inferred from a related field
333
- self.class.many2one_relations.each do |k, field|
334
- model = self.class.load_relation(field.relation, @relations[method_name][0], *arguments)
335
- result = model.relationnal_result(method_name, *arguments)
336
- return result if result
364
+ return false if @relations.has_key?(method_name) and !@relations[method_name]
365
+
366
+ result = relationnal_result(method_name, *arguments)
367
+ if result
368
+ @loaded_relations[method_name] = result
369
+ return result
370
+ elsif !self.class.many2one_relations.empty? #maybe the relation is inherited or could be inferred from a related field
371
+ self.class.many2one_relations.each do |k, field|
372
+ if @relations[k]
373
+ @loaded_relations[k] ||= self.class.load_relation(field.relation, @relations[k][0], self.class.scope_prefix, *arguments)
374
+ model = @loaded_relations[k]
375
+ model.loaded_relations[method_name] ||= model.relationnal_result(method_name, *arguments)
376
+ return model.loaded_relations[method_name] if model.loaded_relations[method_name]
337
377
  end
338
- super
339
378
  end
379
+ super
340
380
  end
381
+
382
+ rescue
383
+ display_available_fields
384
+
341
385
  super
342
386
  end
343
387
 
@@ -10,7 +10,7 @@ end
10
10
  class OpenObjectWizard < OpenObjectLayoutedFields
11
11
  attr_accessor :name, :id, :datas, :arch, :fields, :type, :state, :open_object_resources
12
12
 
13
- def initialize(name, id, data, open_object_resources)
13
+ def initialize(name, id, data, open_object_resources, wizard_context)
14
14
  super(data['arch'], data['fields'])
15
15
  @name = name
16
16
  @id = id
@@ -18,16 +18,17 @@ class OpenObjectWizard < OpenObjectLayoutedFields
18
18
  @datas = data['datas'].symbolize_keys!
19
19
  @type = data['type']
20
20
  @state = data['state']
21
+ @wizard_context = wizard_context
21
22
  end
22
23
 
23
24
  def method_missing(method_symbol, *arguments)
24
25
  values = @datas.merge((arguments[0] || {}).symbolize_keys!)
25
- context = Ooor.global_context.merge(arguments[1] || {})
26
+ context = @wizard_context.merge(arguments[1] || {})
26
27
  if @open_object_resources.size == 1
27
28
  open_object_resource = @open_object_resources[0]
28
29
  data = open_object_resource.class.old_wizard_step(@name, [open_object_resource.id], method_symbol, @id, values, context)
29
30
  if data[1]['state'] == 'end'
30
- return open_object_resource.load(open_object_resource.class.find(open_object_resource.id, :context => context).attributes)
31
+ return open_object_resource.reload_from_record!(open_object_resource.class.find(open_object_resource.id, :context => context))
31
32
  end
32
33
  @arch = data[1]['arch']
33
34
  @fields = data[1]['fields']
@@ -0,0 +1,180 @@
1
+ require 'set'
2
+
3
+ module UML
4
+ #usage: UML.print_uml or with options: UML.print_uml(:all, : detailed) or MyOpenObjectResource.print_uml or UML.print_uml([list_of_classes], :all, :detailed)
5
+
6
+ def self.included(base) base.extend(ClassMethods) end
7
+
8
+ def print_uml(*options)
9
+ UML.print_uml(@config[:models] && @all_loaded_models.select {|model| @config[:models].index(model.openerp_model)} || @all_loaded_models, options)
10
+ end
11
+
12
+ module ClassMethods
13
+ def print_uml(*options)
14
+ UML.print_uml([self], options) if self.is_a?(OpenObjectResource)
15
+ end
16
+ end
17
+
18
+ def self.display_fields(clazz)
19
+ s = ""
20
+ clazz.reload_fields_definition if clazz.fields.empty?
21
+ clazz.fields.sort {|a,b| a[1].ttype <=> b[1].ttype}.each {|i| s << "+ #{i[1].ttype} : #{i[0]}\\l\\n"}
22
+ s
23
+ end
24
+
25
+ def self.print_uml(classes, *options)
26
+ options = options[0] if options[0].is_a?(Array)
27
+ local = (options.index(:all) == nil)
28
+ detailed = (options.index(:detailed) != nil) || local && (options.index(:nodetail) == nil)
29
+
30
+ enabled_targets = classes[0].ooor.config[:models] #defines the scope of the UML for option local
31
+ m2o_edges = {}
32
+ o2m_edges = {}
33
+ m2m_edges = {}
34
+ #instead of diplaying several relations of the same kind between two nodes which would bloat the graph,
35
+ #we track them all and factor them on a common multiline edge label:
36
+ connex_classes = UML.collect_edges(false, local, classes, enabled_targets, m2o_edges, o2m_edges, m2m_edges)
37
+ #back links from connex classes:
38
+ connex_classes += UML.collect_edges(true, local, connex_classes - classes, classes, m2o_edges, o2m_edges, m2m_edges)
39
+
40
+ File.open('uml.dot', 'w') do |f|
41
+ f << <<-eos
42
+ digraph G {
43
+ fontname = "Bitstream Vera Sans"
44
+ fontsize = 8
45
+ label = "*** generated by OOOR by www.akretion.com ***"
46
+ node [
47
+ fontname = "Bitstream Vera Sans"
48
+ fontsize = 16
49
+ shape = "record"
50
+ fillcolor=orange
51
+ style="rounded,filled"
52
+ ]
53
+ edge [
54
+ arrowhead = "none"
55
+ fontname = "Bitstream Vera Sans"
56
+ fontsize = 9
57
+ ]
58
+ eos
59
+
60
+ #UML nodes definitions
61
+ ((connex_classes - classes) + classes - [IrModel, IrModelFields]).each do |model|
62
+ f << " #{model} [ label = \"{#{model.name}#{detailed ? '|' + display_fields(model) : ''}}\" ]"
63
+ end
64
+
65
+ #many2one:
66
+ f << <<-eos
67
+ edge [
68
+ headlabel = "1"
69
+ taillabel = "n"
70
+ ]
71
+ eos
72
+ m2o_edges.each do |k, v|
73
+ reverse_part = v[3].size > 0 ? "\\n/#{v[3].join("\\n")}\"]\n" : "\"]\n"
74
+ f << "edge [label = \"#{v[2].join("\\n")}#{reverse_part}"
75
+ f << "#{v[0]} -> #{v[1]}\n"
76
+ end
77
+
78
+ #one2many:
79
+ f << <<-eos
80
+ edge [
81
+ headlabel = "n"
82
+ taillabel = "1"
83
+ ]
84
+ eos
85
+ o2m_edges.each do |k, v|
86
+ f << "edge [label = \"#{v[3].join("\\n")}\"]\n"
87
+ f << "#{v[0]} -> #{v[1]}\n"
88
+ end
89
+
90
+ #many2many:
91
+ f << <<-eos
92
+ edge [
93
+ headlabel = "n"
94
+ taillabel = "n"
95
+ ]
96
+ eos
97
+ m2m_edges.each do |k, v|
98
+ reverse_part = v[3].size > 0 ? "\\n/#{v[3].join("\\n")}\"]\n" : "\"]\n"
99
+ f << "edge [label = \"#{v[2].join("\\n")}}#{reverse_part}"
100
+ f << "#{v[0]} -> #{v[1]}\n"
101
+ end
102
+
103
+ f << "}"
104
+ end
105
+
106
+ begin
107
+ cmd_line1 = "rm uml.png"
108
+ system(cmd_line1)
109
+ rescue
110
+ end
111
+ cmd_line2 = "dot -Tpng uml.dot -o uml.png"
112
+ system(cmd_line2)
113
+ end
114
+
115
+ def self.collect_edges(is_reverse, local, classes, enabled_targets, m2o_edges, o2m_edges, m2m_edges)
116
+ connex_classes = Set.new
117
+
118
+ classes.each do |model|
119
+ model.reload_fields_definition if model.fields.empty?
120
+
121
+ #many2one:
122
+ model.many2one_relations.each do |k, field|
123
+ target = UML.get_target(is_reverse, local, enabled_targets, field, model)
124
+ if target
125
+ connex_classes.add(target)
126
+ if m2o_edges["#{model}-#{target}"]
127
+ m2o_edges["#{model}-#{target}"][2] += [k]
128
+ else
129
+ m2o_edges["#{model}-#{target}"] = [model, target, [k], []]
130
+ end
131
+ end
132
+
133
+ end
134
+ end
135
+
136
+ classes.each do |model|
137
+ #one2many:
138
+ model.one2many_relations.each do |k, field|
139
+ target = UML.get_target(is_reverse, local, enabled_targets, field, model)
140
+ if target
141
+ connex_classes.add(target)
142
+ if m2o_edges["#{target}-#{model}"]
143
+ m2o_edges["#{target}-#{model}"][3] += [k]
144
+ elsif o2m_edges["#{model}-#{target}"]
145
+ o2m_edges["#{model}-#{target}"][3] += [k]
146
+ else
147
+ o2m_edges["#{model}-#{target}"] = [model, target, [], [k]]
148
+ end
149
+ end
150
+ end
151
+
152
+ #many2many:
153
+ model.many2many_relations.each do |k, field|
154
+ target = UML.get_target(is_reverse, local, enabled_targets, field, model)
155
+ if target
156
+ connex_classes.add(target)
157
+ if m2m_edges["#{model}-#{target}"]
158
+ m2m_edges["#{model}-#{target}"][2] += [k]
159
+ elsif m2m_edges["#{target}-#{model}"]
160
+ m2m_edges["#{target}-#{model}"][3] += [k]
161
+ else
162
+ m2m_edges["#{model}-#{target}"] = [model, target, [k], []]
163
+ end
164
+ end
165
+ end
166
+
167
+ end
168
+ connex_classes
169
+ end
170
+
171
+ private
172
+
173
+ def self.get_target(is_reverse, local, enabled_targets, field, model)
174
+ if (is_reverse && !local) || (!enabled_targets) || enabled_targets.index(field.relation)
175
+ target_name = model.class_name_from_model_key(field.relation)
176
+ return Object.const_defined?(target_name) ? Object.const_get(target_name) : model.ooor.define_openerp_model(field.relation, nil, nil, nil, nil, model.scope_prefix)
177
+ end
178
+ return false
179
+ end
180
+ end
@@ -1,89 +1,95 @@
1
1
  require 'logger'
2
2
  require 'xmlrpc/client'
3
+ require 'app/models/open_object_resource'
4
+ require 'app/models/uml'
5
+
6
+ class Ooor
7
+ include UML
8
+
9
+ attr_accessor :logger, :config, :all_loaded_models, :base_url, :global_context, :ir_model_class
10
+
11
+ #load the custom configuration
12
+ def self.load_config(config_file=nil, env=nil)
13
+ config_file ||= defined?(RAILS_ROOT) && "#{RAILS_ROOT}/config/ooor.yml" || 'ooor.yml'
14
+ @config = YAML.load_file(config_file)[env || 'development']
15
+ rescue SystemCallError
16
+ @logger.error """failed to load OOOR yaml configuration file.
17
+ make sure your app has a #{config_file} file correctly set up
18
+ if not, just copy/paste the default ooor.yml file from the OOOR Gem
19
+ to #{RAILS_ROOT}/config/ooor.yml and customize it properly\n\n"""
20
+ raise
21
+ end
3
22
 
4
- module Ooor
5
-
6
- class << self
7
-
8
- attr_accessor :logger, :config, :all_loaded_models, :binding, :base_url, :global_context
9
-
10
- #load the custom configuration
11
- def load_config(config_file=nil, env=nil)
12
- config_file ||= defined?(RAILS_ROOT) && "#{RAILS_ROOT}/config/ooor.yml" || 'ooor.yml'
13
- env ||= defined?(RAILS_ENV) && RAILS_ENV || 'development'
14
- Ooor.config = YAML.load_file(config_file)[env]
15
- rescue SystemCallError
16
- Ooor.logger.error """failed to load OOOR yaml configuration file.
17
- make sure your app has a #{config_file} file correctly set up
18
- if not, just copy/paste the default ooor.yml file from the OOOR Gem
19
- to #{RAILS_ROOT}/config/ooor.yml and customize it properly\n\n"""
20
- raise
21
- end
22
-
23
- def loaded?
24
- Ooor.all_loaded_models.size > 0
23
+ def global_login(user, password)
24
+ begin
25
+ @config[:username] = user
26
+ @config[:password] = password
27
+ client = OpenObjectResource.client(@base_url + "/common")
28
+ OpenObjectResource.try_with_pretty_error_log { client.call("login", @config[:database], user, password)}
29
+ rescue SocketError => error
30
+ @logger.error """login to OpenERP server failed:
31
+ #{error.inspect}
32
+ Are your sure the server is started? Are your login parameters correct? Can this server ping the OpenERP server?
33
+ login XML/RPC url was #{@config[:url].gsub(/\/$/,'') + "/common"}"""
25
34
  end
35
+ end
26
36
 
27
- def global_login(user, password)
28
- begin
29
- Ooor.config[:username] = user
30
- Ooor.config[:password] = password
31
- client = OpenObjectResource.client(Ooor.base_url + "/common")
32
- OpenObjectResource.try_with_pretty_error_log { client.call("login", Ooor.config[:database], user, password)}
33
- rescue SocketError => error
34
- Ooor.logger.error """login to OpenERP server failed:
35
- #{error.inspect}
36
- Are your sure the server is started? Are your login parameters correct? Can this server ping the OpenERP server?
37
- login XML/RPC url was #{Ooor.config[:url].gsub(/\/$/,'') + "/common"}"""
38
- end
37
+ def initialize(config, env=false)
38
+ @config = config.is_a?(String) ? Ooor.load_config(config, env) : config
39
+ @config.symbolize_keys!
40
+ @logger = ((defined?(RAILS_ENV) and RAILS_ENV != "development") ? Rails.logger : Logger.new(STDOUT))
41
+ @logger.level = config[:log_level] if config[:log_level]
42
+ @base_url = config[:url].gsub(/\/$/,'')
43
+ @global_context = config[:global_context] || {}
44
+ config[:user_id] = global_login(config[:username] || 'admin', config[:password] || 'admin')
45
+
46
+ @all_loaded_models = []
47
+ OpenObjectResource.logger = @logger
48
+ @ir_model_class = define_openerp_model("ir.model", nil, nil, nil, nil, config[:scope_prefix] || '')
49
+ define_openerp_model("ir.model.fields", nil, nil, nil, nil, config[:scope_prefix] || '')
50
+
51
+ if config[:models] #we load only a customized subset of the OpenERP models
52
+ models = @ir_model_class.find(:all, :domain => [['model', 'in', config[:models]]])
53
+ else #we load all the models
54
+ models = @ir_model_class.find(:all).reject {|model| model.model == "ir.model" || model.model == "ir.model.fields"}
39
55
  end
56
+ models.each {|openerp_model| define_openerp_model(openerp_model, nil, nil, nil, nil, config[:scope_prefix] || '')}
57
+ end
40
58
 
41
- def reload!(config=false, env=false, keep_config=false)
42
- Ooor.config = config.is_a?(Hash) && config or keep_config && Ooor.config or self.load_config(config, env)
43
- Ooor.config.symbolize_keys!
44
- Ooor.logger.level = Ooor.config[:log_level] if Ooor.config[:log_level]
45
- Ooor.base_url = Ooor.config[:url].gsub(/\/$/,'')
46
- Ooor.global_context = Ooor.config[:global_context] || {}
47
- Ooor.config[:user_id] = global_login(Ooor.config[:username] || 'admin', Ooor.config[:password] || 'admin')
48
-
49
- #*************** load the models
50
-
51
- Ooor.all_loaded_models = []
52
- OpenObjectResource.logger = Ooor.logger
53
- OpenObjectResource.define_openerp_model("ir.model", nil, nil, nil, nil, Ooor.binding)
54
- OpenObjectResource.define_openerp_model("ir.model.fields", nil, nil, nil, nil, Ooor.binding)
55
-
56
- if Ooor.config[:models] #we load only a customized subset of the OpenERP models
57
- models = IrModel.find(:all, :domain => [['model', 'in', Ooor.config[:models]]])
58
- else #we load all the models
59
- models = IrModel.find(:all)
60
- end
61
-
62
- models.each {|openerp_model| OpenObjectResource.define_openerp_model(openerp_model, nil, nil, nil, nil, Ooor.binding)}
63
-
64
- # *************** load the models REST controllers
65
- if defined?(ActionController)
66
- OpenObjectsController.logger = Ooor.logger
67
- models.each {|openerp_model| OpenObjectsController.define_openerp_controller(openerp_model.model, Ooor.binding) }
68
- end
69
-
59
+ def define_openerp_model(arg, url, database, user_id, pass, scope_prefix)
60
+ if arg.is_a?(String) && arg != 'ir.model' && arg != 'ir.model.fields'
61
+ arg = @ir_model_class.find(:first, :domain => [['model', '=', arg]])
70
62
  end
71
-
63
+ param = (arg.is_a? OpenObjectResource) ? arg.attributes.merge(arg.relations) : {'model' => arg}
64
+ klass = Class.new(OpenObjectResource)
65
+ klass.ooor = self
66
+ klass.site = url || @base_url
67
+ klass.user = user_id
68
+ klass.password = pass
69
+ klass.openerp_database = database
70
+ klass.openerp_model = param['model']
71
+ klass.openerp_id = url || param['id']
72
+ klass.info = (param['info'] || '').gsub("'",' ')
73
+ klass.name = param['name']
74
+ klass.state = param['state']
75
+ klass.field_ids = param['field_id']
76
+ klass.access_ids = param['access_ids']
77
+ klass.many2one_relations = {}
78
+ klass.one2many_relations = {}
79
+ klass.many2many_relations = {}
80
+ klass.fields = {}
81
+ klass.scope_prefix = scope_prefix
82
+ model_class_name = klass.class_name_from_model_key
83
+ @logger.info "registering #{model_class_name} as a Rails ActiveResource Model wrapper for OpenObject #{param['model']} model"
84
+ Object.const_set(model_class_name, klass)
85
+ @all_loaded_models.push(klass)
86
+ klass
72
87
  end
73
88
 
74
89
  end
75
90
 
76
-
77
- Ooor.logger = ((defined?(RAILS_ENV) and RAILS_ENV != "development") ? Rails.logger : Logger.new(STDOUT))
78
- Ooor.binding = lambda {}
79
-
80
- require 'app/models/open_object_resource'
81
- require 'app/controllers/open_objects_controller'
82
-
83
-
91
+ #Optionnal Rails settings:
84
92
  if defined?(Rails)
85
- include Ooor
86
- if Ooor.load_config['bootstrap']
87
- Ooor.reload!(false, false, true)
88
- end
93
+ config = Ooor.load_config(false, RAILS_ENV)
94
+ OOOR = Ooor.new(config) if config['bootstrap']
89
95
  end
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.1.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Raphael Valyi - www.akretion.com
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-12-16 00:00:00 -02:00
12
+ date: 2010-01-18 00:00:00 -02:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -36,7 +36,7 @@ files:
36
36
  - lib/ooor.rb
37
37
  - lib/app/models/open_object_resource.rb
38
38
  - lib/app/models/open_object_ui.rb
39
- - lib/app/controllers/open_objects_controller.rb
39
+ - lib/app/models/uml.rb
40
40
  - ooor.yml
41
41
  has_rdoc: true
42
42
  homepage: http://github.com/rvalyi/ooor
@@ -1,139 +0,0 @@
1
- require 'action_controller'
2
-
3
- class OpenObjectsController < ActionController::Base
4
-
5
- #TODO get lang from URL in before filter
6
- #TODO use timezone from HTTP header and put is in context
7
-
8
- # ******************** class methods ********************
9
- class << self
10
-
11
- cattr_accessor :logger
12
-
13
- def model_class
14
- if defined?(@model_class)
15
- @model_class
16
- elsif superclass != Object && superclass.model_class
17
- superclass.model_class.dup.freeze
18
- end
19
- end
20
-
21
- def model_class=(_model_class)
22
- @model_class = _model_class
23
- end
24
-
25
- def ids_from_param(param)
26
- if param.split(',').size > 0
27
- return eval param
28
- else
29
- return param
30
- end
31
- end
32
-
33
- def define_openerp_controller(model_key, binding)
34
- model_class_name = OpenObjectResource.class_name_from_model_key(model_key)
35
- controller_class_name = model_class_name + "Controller"
36
- logger.info "registering #{controller_class_name} as a Rails ActiveResource Controller wrapper for OpenObject #{model_key} model"
37
- eval "
38
- class #{controller_class_name} < OpenObjectsController
39
- self.model_class = #{model_class_name}
40
- end
41
- ", binding
42
- end
43
-
44
- def load_all_controllers(map)
45
- Ooor.all_loaded_models.each do |model|
46
- map.resources model.gsub('.', '_').to_sym
47
- end
48
- end
49
-
50
- end
51
-
52
-
53
- # ******************** instance methods ********************
54
-
55
- # GET /models
56
- # GET /models.xml
57
- def index
58
- @models = self.class.model_class.find(:all)
59
-
60
- respond_to do |format|
61
- format.html # index.html.erb
62
- format.xml { render :xml => @models }
63
- end
64
- end
65
-
66
- # GET /models/1
67
- # GET /models/1.xml
68
- def show
69
- @models = self.class.model_class.find(self.class.ids_from_param(params[:id]))
70
-
71
- respond_to do |format|
72
- format.html # show.html.erb
73
- format.xml { render :xml => @models }
74
- format.json { render :json => @models }
75
- end
76
- end
77
-
78
- # GET /models/new
79
- # GET /models/new.xml
80
- def new
81
- @models = self.class.model_class.new
82
-
83
- respond_to do |format|
84
- format.html # new.html.erb
85
- format.xml { render :xml => @models }
86
- end
87
- end
88
-
89
- # GET /models/1/edit
90
- def edit
91
- @models = self.class.model_class.find(params[:id])
92
- end
93
-
94
- # POST /models
95
- # POST /models.xml
96
- def create
97
- @models = self.class.model_class.new(params[:partners])
98
-
99
- respond_to do |format|
100
- if @models.save
101
- flash[:notice] = 'Model was successfully created.'
102
- format.html { redirect_to(@models) }
103
- format.xml { render :xml => @models, :status => :created, :location => @models }
104
- else
105
- format.html { render :action => "new" }
106
- format.xml { render :xml => @models.errors, :status => :unprocessable_entity }
107
- end
108
- end
109
- end
110
-
111
- # PUT /models/1
112
- # PUT /models/1.xml
113
- def update
114
- @models = self.class.model_class.find(params[:id])
115
-
116
- respond_to do |format|
117
- if @models.update_attributes(params[:partners])
118
- flash[:notice] = 'Partners was successfully updated.'
119
- format.html { redirect_to(@models) }
120
- format.xml { head :ok }
121
- else
122
- format.html { render :action => "edit" }
123
- format.xml { render :xml => @models.errors, :status => :unprocessable_entity }
124
- end
125
- end
126
- end
127
-
128
- # DELETE /models/1
129
- # DELETE /models/1.xml
130
- def destroy
131
- @models = self.class.model_class.find(params[:id])
132
- @models.destroy
133
-
134
- respond_to do |format|
135
- format.html { redirect_to(url_for(:action => index)) }
136
- format.xml { head :ok }
137
- end
138
- end
139
- end