ooor 2.0.4 → 2.0.5
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.
- checksums.yaml +4 -4
- data/lib/ooor.rb +7 -2
- data/lib/ooor/associations.rb +37 -13
- data/lib/ooor/autosave_association.rb +197 -0
- data/lib/ooor/base.rb +21 -28
- data/lib/ooor/errors.rb +1 -1
- data/lib/ooor/field_methods.rb +37 -27
- data/lib/ooor/mini_active_resource.rb +16 -22
- data/lib/ooor/model_registry.rb +6 -1
- data/lib/ooor/model_schema.rb +25 -0
- data/lib/ooor/naming.rb +27 -13
- data/lib/ooor/nested_attributes.rb +57 -0
- data/lib/ooor/persistence.rb +247 -49
- data/lib/ooor/reflection.rb +9 -7
- data/lib/ooor/reflection_ooor.rb +17 -17
- data/lib/ooor/relation.rb +0 -1
- data/lib/ooor/relation/finder_methods.rb +4 -4
- data/lib/ooor/report.rb +9 -9
- data/lib/ooor/services.rb +1 -1
- data/lib/ooor/session.rb +72 -22
- data/lib/ooor/session_handler.rb +17 -21
- data/lib/ooor/type_casting.rb +34 -37
- data/lib/ooor/version.rb +1 -1
- data/spec/ooor_spec.rb +192 -43
- metadata +7 -9
- data/lib/ooor/connection.rb +0 -37
- data/spec/install_nightly.sh +0 -17
- data/spec/requirements.txt +0 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 68bb22d821a0337958de6835edebec32672e31d5
|
4
|
+
data.tar.gz: e515ccba848078b48a39b2ab22dae262dd717bda
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 070ab8305192ceaa8c77c959c656c89bdd06f2250e3818a2ed088f5a6920bfda147420d583e748d5ceb2ddfec0dbe7f826ea37bf068bc92cbc2a8784028fd294
|
7
|
+
data.tar.gz: fdf089b8708e2e6912945976f3d85bf328af15e62bbe8e48e08553ddc0bd8887c2ca06b788ee389831424df8a70fec0a17ec5919e1feee3bfd6fe4fef73ea39a
|
data/lib/ooor.rb
CHANGED
@@ -12,7 +12,10 @@ require 'logger'
|
|
12
12
|
module Ooor
|
13
13
|
extend ActiveSupport::Autoload
|
14
14
|
autoload :Base
|
15
|
+
autoload :ModelSchema
|
15
16
|
autoload :Persistence
|
17
|
+
autoload :AutosaveAssociation
|
18
|
+
autoload :NestedAttributes
|
16
19
|
autoload :Callbacks
|
17
20
|
autoload :Cache, 'active_support/cache'
|
18
21
|
autoload :Serialization
|
@@ -96,8 +99,10 @@ module Ooor
|
|
96
99
|
end
|
97
100
|
|
98
101
|
|
99
|
-
def with_ooor_session(config={}, id
|
100
|
-
|
102
|
+
def with_ooor_session(config={}, id=:noweb)
|
103
|
+
session = Ooor.session_handler.retrieve_session(config, id)
|
104
|
+
Ooor.session_handler.register_session(session)
|
105
|
+
yield session
|
101
106
|
end
|
102
107
|
|
103
108
|
def with_ooor_default_session(config={})
|
data/lib/ooor/associations.rb
CHANGED
@@ -26,25 +26,49 @@ module Ooor
|
|
26
26
|
|
27
27
|
# fakes associations like much like ActiveRecord according to the cached OpenERP data model
|
28
28
|
def relationnal_result(method_name, *arguments)
|
29
|
-
self.class.reload_fields_definition(false
|
29
|
+
self.class.reload_fields_definition(false)
|
30
30
|
if self.class.many2one_associations.has_key?(method_name)
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
31
|
+
load_m2o_association(method_name, *arguments)
|
32
|
+
elsif self.class.polymorphic_m2o_associations.has_key?(method_name)# && @associations[method_name]
|
33
|
+
load_polymorphic_m2o_association(method_name, *arguments)
|
34
|
+
# values = @associations[method_name].split(',')
|
35
|
+
# self.class.const_get(values[0]).find(values[1], arguments.extract_options!)
|
36
|
+
else # o2m or m2m
|
37
|
+
load_x2m_association(method_name, *arguments)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def load_polymorphic_m2o_association(method_name, *arguments)
|
44
|
+
if @associations[method_name]
|
39
45
|
values = @associations[method_name].split(',')
|
40
46
|
self.class.const_get(values[0]).find(values[1], arguments.extract_options!)
|
41
|
-
else
|
42
|
-
|
43
|
-
|
47
|
+
else
|
48
|
+
false
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def load_m2o_association(method_name, *arguments)
|
53
|
+
if !@associations[method_name]
|
54
|
+
nil
|
55
|
+
else
|
56
|
+
if @associations[method_name].is_a?(Integer)
|
57
|
+
id = @associations[method_name]
|
58
|
+
display_name = nil
|
59
|
+
else
|
60
|
+
id = @associations[method_name][0]
|
61
|
+
display_name = @associations[method_name][1]
|
62
|
+
end
|
63
|
+
rel = self.class.many2one_associations[method_name]['relation']
|
64
|
+
self.class.const_get(rel).new({id: id, _display_name: display_name}, [], true, false, true)
|
65
|
+
# self.class.const_get(rel).find(id, arguments.extract_options!)
|
44
66
|
end
|
45
67
|
end
|
46
68
|
|
47
|
-
def load_x2m_association(
|
69
|
+
def load_x2m_association(method_name, *arguments)
|
70
|
+
model_key = self.class.all_fields[method_name]['relation']
|
71
|
+
ids = @associations[method_name] || []
|
48
72
|
options = arguments.extract_options!
|
49
73
|
related_class = self.class.const_get(model_key)
|
50
74
|
CollectionProxy.new(related_class, {}).apply_finder_options(options.merge(ids: ids))
|
@@ -0,0 +1,197 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
require 'active_support/concern'
|
3
|
+
|
4
|
+
module Ooor
|
5
|
+
# = Ooor Autosave Association, adapted from ActiveRecord 4.1
|
6
|
+
#
|
7
|
+
# +AutosaveAssociation+ is a module that takes care of automatically saving
|
8
|
+
# associated records when their parent is saved. In addition to saving, it
|
9
|
+
# also destroys any associated records that were marked for destruction.
|
10
|
+
# (See +mark_for_destruction+ and <tt>marked_for_destruction?</tt>).
|
11
|
+
#
|
12
|
+
# Saving of the parent, its associations, and the destruction of marked
|
13
|
+
# associations, all happen inside a transaction. This should never leave the
|
14
|
+
# database in an inconsistent state.
|
15
|
+
#
|
16
|
+
# If validations for any of the associations fail, their error messages will
|
17
|
+
# be applied to the parent (TODO)
|
18
|
+
module AutosaveAssociation
|
19
|
+
extend ActiveSupport::Concern
|
20
|
+
|
21
|
+
module ClassMethods
|
22
|
+
private
|
23
|
+
|
24
|
+
# same as ActiveRecord
|
25
|
+
def define_non_cyclic_method(name, &block)
|
26
|
+
define_method(name) do |*args|
|
27
|
+
result = true; @_already_called ||= {}
|
28
|
+
# Loop prevention for validation of associations
|
29
|
+
unless @_already_called[name]
|
30
|
+
begin
|
31
|
+
@_already_called[name]=true
|
32
|
+
result = instance_eval(&block)
|
33
|
+
ensure
|
34
|
+
@_already_called[name]=false
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
result
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Adds validation and save callbacks for the association as specified by
|
43
|
+
# the +reflection+.
|
44
|
+
#
|
45
|
+
# For performance reasons, we don't check whether to validate at runtime.
|
46
|
+
# However the validation and callback methods are lazy and those methods
|
47
|
+
# get created when they are invoked for the very first time. However,
|
48
|
+
# this can change, for instance, when using nested attributes, which is
|
49
|
+
# called _after_ the association has been defined. Since we don't want
|
50
|
+
# the callbacks to get defined multiple times, there are guards that
|
51
|
+
# check if the save or validation methods have already been defined
|
52
|
+
# before actually defining them.
|
53
|
+
def add_autosave_association_callbacks(reflection) # TODO add support for m2o
|
54
|
+
save_method = :"autosave_associated_records_for_#{reflection.name}"
|
55
|
+
validation_method = :"validate_associated_records_for_#{reflection.name}"
|
56
|
+
collection = true #reflection.collection?
|
57
|
+
unless method_defined?(save_method)
|
58
|
+
if collection
|
59
|
+
before_save :before_save_collection_association
|
60
|
+
define_non_cyclic_method(save_method) { save_collection_association(reflection) }
|
61
|
+
before_save save_method
|
62
|
+
# NOTE Ooor is different from ActiveRecord here: we run the nested callbacks before saving
|
63
|
+
# the whole hash of values including the nested records
|
64
|
+
# Doesn't use after_save as that would save associations added in after_create/after_update twice
|
65
|
+
# after_create save_method
|
66
|
+
# after_update save_method
|
67
|
+
else
|
68
|
+
raise raise ArgumentError, "Not implemented in Ooor; seems OpenERP won't support such nested attribute in the same transaction anyhow"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
if reflection.validate? && !method_defined?(validation_method)
|
73
|
+
method = (collection ? :validate_collection_association : :validate_single_association)
|
74
|
+
define_non_cyclic_method(validation_method) { send(method, reflection) }
|
75
|
+
validate validation_method
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Reloads the attributes of the object as usual and clears <tt>marked_for_destruction</tt> flag.
|
81
|
+
def reload(options = nil)
|
82
|
+
@marked_for_destruction = false
|
83
|
+
@destroyed_by_association = nil
|
84
|
+
super
|
85
|
+
end
|
86
|
+
|
87
|
+
# Marks this record to be destroyed as part of the parents save transaction.
|
88
|
+
# This does _not_ actually destroy the record instantly, rather child record will be destroyed
|
89
|
+
# when <tt>parent.save</tt> is called.
|
90
|
+
#
|
91
|
+
# Only useful if the <tt>:autosave</tt> option on the parent is enabled for this associated model.
|
92
|
+
def mark_for_destruction
|
93
|
+
@marked_for_destruction = true
|
94
|
+
end
|
95
|
+
|
96
|
+
# Returns whether or not this record will be destroyed as part of the parents save transaction.
|
97
|
+
#
|
98
|
+
# Only useful if the <tt>:autosave</tt> option on the parent is enabled for this associated model.
|
99
|
+
def marked_for_destruction?
|
100
|
+
@marked_for_destruction
|
101
|
+
end
|
102
|
+
|
103
|
+
# Records the association that is being destroyed and destroying this
|
104
|
+
# record in the process.
|
105
|
+
def destroyed_by_association=(reflection)
|
106
|
+
@destroyed_by_association = reflection
|
107
|
+
end
|
108
|
+
|
109
|
+
# Returns the association for the parent being destroyed.
|
110
|
+
#
|
111
|
+
# Used to avoid updating the counter cache unnecessarily.
|
112
|
+
def destroyed_by_association
|
113
|
+
@destroyed_by_association
|
114
|
+
end
|
115
|
+
|
116
|
+
# Returns whether or not this record has been changed in any way (including whether
|
117
|
+
# any of its nested autosave associations are likewise changed)
|
118
|
+
def changed_for_autosave?
|
119
|
+
new_record? || changed? || marked_for_destruction? # TODO || nested_records_changed_for_autosave?
|
120
|
+
end
|
121
|
+
|
122
|
+
private
|
123
|
+
|
124
|
+
# Returns the record for an association collection that should be validated
|
125
|
+
# or saved. If +autosave+ is +false+ only new records will be returned,
|
126
|
+
# unless the parent is/was a new record itself.
|
127
|
+
def associated_records_to_validate_or_save(association, new_record, autosave)
|
128
|
+
if new_record
|
129
|
+
association && association.target
|
130
|
+
elsif autosave
|
131
|
+
association.target.find_all { |record| record.changed_for_autosave? }
|
132
|
+
else
|
133
|
+
association.target.find_all { |record| record.new_record? }
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# go through nested autosave associations that are loaded in memory (without loading
|
138
|
+
# any new ones), and return true if is changed for autosave
|
139
|
+
# def nested_records_changed_for_autosave?
|
140
|
+
# self.class.reflect_on_all_autosave_associations.any? do |reflection|
|
141
|
+
# association = association_instance_get(reflection.name)
|
142
|
+
# association && Array.wrap(association.target).any? { |a| a.changed_for_autosave? }
|
143
|
+
# end
|
144
|
+
# end
|
145
|
+
|
146
|
+
# Is used as a before_save callback to check while saving a collection
|
147
|
+
# association whether or not the parent was a new record before saving.
|
148
|
+
def before_save_collection_association
|
149
|
+
@new_record_before_save = new_record?
|
150
|
+
true
|
151
|
+
end
|
152
|
+
|
153
|
+
# Saves any new associated records, or all loaded autosave associations if
|
154
|
+
# <tt>:autosave</tt> is enabled on the association.
|
155
|
+
#
|
156
|
+
# In addition, it destroys all children that were marked for destruction
|
157
|
+
# with mark_for_destruction.
|
158
|
+
#
|
159
|
+
# This all happens inside a transaction, _if_ the Transactions module is included into
|
160
|
+
# ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
|
161
|
+
def save_collection_association(reflection)
|
162
|
+
# if association = association_instance_get(reflection.name)
|
163
|
+
if target = @loaded_associations[reflection.name] #TODO use a real Association wrapper
|
164
|
+
association = OpenStruct.new(target: target)
|
165
|
+
autosave = reflection.options[:autosave]
|
166
|
+
|
167
|
+
if records = associated_records_to_validate_or_save(association, @new_record_before_save, autosave)
|
168
|
+
# NOTE saving the object with its nested associations will properly destroy records in OpenERP
|
169
|
+
# no need to do it now like in ActiveRecord
|
170
|
+
records.each do |record|
|
171
|
+
next if record.destroyed?
|
172
|
+
|
173
|
+
saved = true
|
174
|
+
|
175
|
+
if autosave != false && (@new_record_before_save || record.new_record?)
|
176
|
+
if autosave
|
177
|
+
# saved = association.insert_record(record, false)
|
178
|
+
record.run_callbacks(:save) { false }
|
179
|
+
record.run_callbacks(:create) { false }
|
180
|
+
# else
|
181
|
+
# association.insert_record(record) unless reflection.nested?
|
182
|
+
end
|
183
|
+
elsif autosave
|
184
|
+
record.run_callbacks(:save) {false}
|
185
|
+
record.run_callbacks(:update) {false}
|
186
|
+
# saved = record.save(:validate => false)
|
187
|
+
end
|
188
|
+
|
189
|
+
end
|
190
|
+
end
|
191
|
+
# reconstruct the scope now that we know the owner's id
|
192
|
+
# association.reset_scope if association.respond_to?(:reset_scope)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
end
|
197
|
+
end
|
data/lib/ooor/base.rb
CHANGED
@@ -12,36 +12,19 @@ require 'ooor/errors'
|
|
12
12
|
|
13
13
|
module Ooor
|
14
14
|
|
15
|
-
# meta data shared across sessions, a cache of the data in ir_model in OpenERP.
|
16
|
-
# reused accross workers in a multi-process web app (via memcache for instance).
|
17
|
-
class ModelTemplate
|
18
|
-
|
19
|
-
TEMPLATE_PROPERTIES = [:name, :openerp_id, :info, :access_ids, :description,
|
20
|
-
:openerp_model, :field_ids, :state, :fields,
|
21
|
-
:many2one_associations, :one2many_associations, :many2many_associations,
|
22
|
-
:polymorphic_m2o_associations, :associations_keys,
|
23
|
-
:associations, :columns]
|
24
|
-
|
25
|
-
attr_accessor *TEMPLATE_PROPERTIES, :columns_hash
|
26
|
-
end
|
27
|
-
|
28
15
|
# the base class for proxies to OpenERP objects
|
29
16
|
class Base < Ooor::MiniActiveResource
|
30
17
|
include Naming, TypeCasting, Serialization, ReflectionOoor, Reflection
|
31
|
-
include Associations, Report, FinderMethods, FieldMethods
|
18
|
+
include Associations, Report, FinderMethods, FieldMethods, AutosaveAssociation, NestedAttributes
|
32
19
|
|
33
20
|
# ********************** class methods ************************************
|
34
21
|
class << self
|
35
22
|
|
36
|
-
attr_accessor
|
37
|
-
delegate *
|
23
|
+
attr_accessor :name, :session, :t, :scope_prefix
|
24
|
+
delegate *ModelSchema::TEMPLATE_PROPERTIES, to: :t
|
38
25
|
|
39
26
|
# ******************** remote communication *****************************
|
40
27
|
|
41
|
-
def create(attributes = {}, context={}, default_get_list=false, reload=true)
|
42
|
-
self.new(attributes, default_get_list, context).tap { |resource| resource.save(context, reload) }
|
43
|
-
end
|
44
|
-
|
45
28
|
#OpenERP search method
|
46
29
|
def search(domain=[], offset=0, limit=false, order=false, context={}, count=false)
|
47
30
|
rpc_execute(:search, to_openerp_domain(domain), offset, limit, order, context, count)
|
@@ -60,8 +43,12 @@ module Ooor
|
|
60
43
|
end
|
61
44
|
|
62
45
|
def object_service(service, obj, method, *args)
|
63
|
-
reload_fields_definition(false
|
64
|
-
cast_answer_to_ruby!(
|
46
|
+
reload_fields_definition(false)
|
47
|
+
cast_answer_to_ruby!(session.object.object_service(service, obj, method, *cast_request_to_openerp(args)))
|
48
|
+
end
|
49
|
+
|
50
|
+
def context
|
51
|
+
session.session_context
|
65
52
|
end
|
66
53
|
|
67
54
|
def method_missing(method_symbol, *args)
|
@@ -71,8 +58,6 @@ module Ooor
|
|
71
58
|
|
72
59
|
# ******************** AREL Minimal implementation ***********************
|
73
60
|
|
74
|
-
def relation(context={}); @relation ||= Relation.new(self, context); end #TODO template
|
75
|
-
def scoped(context={}); relation(context); end
|
76
61
|
def where(opts, *rest); relation.where(opts, *rest); end
|
77
62
|
def all(*args); relation.all(*args); end
|
78
63
|
def limit(value); relation.limit(value); end
|
@@ -83,18 +68,22 @@ module Ooor
|
|
83
68
|
|
84
69
|
def logger; Ooor.logger; end
|
85
70
|
|
71
|
+
private
|
72
|
+
|
73
|
+
def relation; @relation ||= Relation.new(self); end
|
74
|
+
|
86
75
|
end
|
87
76
|
|
88
77
|
self.name = "Base"
|
89
78
|
|
90
79
|
# ********************** instance methods **********************************
|
91
80
|
|
92
|
-
attr_accessor :associations, :loaded_associations, :ir_model_data_id
|
81
|
+
attr_accessor :associations, :loaded_associations, :ir_model_data_id
|
93
82
|
|
94
83
|
include Persistence, Callbacks, ActiveModel::Dirty
|
95
84
|
|
96
85
|
def rpc_execute(method, *args)
|
97
|
-
args += [self.class.
|
86
|
+
args += [self.class.context] unless args[-1].is_a? Hash
|
98
87
|
self.class.object_service(:execute, self.class.openerp_model, method, *args)
|
99
88
|
end
|
100
89
|
|
@@ -111,8 +100,8 @@ module Ooor
|
|
111
100
|
|
112
101
|
#wrapper for OpenERP exec_workflow Business Process Management engine
|
113
102
|
def wkf_action(action, context={}, reload=true)
|
114
|
-
self.class.object_service(:exec_workflow, self.class.openerp_model, action, self.id,
|
115
|
-
reload_fields
|
103
|
+
self.class.object_service(:exec_workflow, self.class.openerp_model, action, self.id, context)
|
104
|
+
reload_fields if reload
|
116
105
|
end
|
117
106
|
|
118
107
|
#Add get_report_data to obtain [report["result"],report["format]] of a concrete openERP Object
|
@@ -124,6 +113,10 @@ module Ooor
|
|
124
113
|
|
125
114
|
private
|
126
115
|
|
116
|
+
def context
|
117
|
+
self.class.context
|
118
|
+
end
|
119
|
+
|
127
120
|
# Ruby 1.9.compat, See also http://tenderlovemaking.com/2011/06/28/til-its-ok-to-return-nil-from-to_ary/
|
128
121
|
def to_ary; nil; end # :nodoc:
|
129
122
|
|
data/lib/ooor/errors.rb
CHANGED
@@ -25,7 +25,7 @@ module Ooor
|
|
25
25
|
return ValueError.new(method, faultCode, faultString, *args)
|
26
26
|
elsif faultCode =~ /ValidateError/
|
27
27
|
return ValidationError.new(method, faultCode, faultString, *args)
|
28
|
-
elsif faultCode =~ /AccessDenied/
|
28
|
+
elsif faultCode =~ /AccessDenied/ || faultCode =~ /Access Denied/
|
29
29
|
return UnAuthorizedError.new(method, faultCode, faultString, *args)
|
30
30
|
elsif faultCode =~ /AuthenticationError: Credentials not provided/
|
31
31
|
return InvalidSessionError.new(method, faultCode, faultString, *args)
|
data/lib/ooor/field_methods.rb
CHANGED
@@ -6,7 +6,7 @@ module Ooor
|
|
6
6
|
|
7
7
|
module ClassMethods
|
8
8
|
|
9
|
-
def reload_fields_definition(force=false
|
9
|
+
def reload_fields_definition(force=false)
|
10
10
|
if force || !fields
|
11
11
|
@t.fields = {}
|
12
12
|
@columns_hash = {}
|
@@ -14,7 +14,7 @@ module Ooor
|
|
14
14
|
fields_get.each { |k, field| reload_field_definition(k, field) }
|
15
15
|
@t.associations_keys = many2one_associations.keys + one2many_associations.keys + many2many_associations.keys + polymorphic_m2o_associations.keys
|
16
16
|
logger.debug "#{fields.size} fields loaded in model #{self.name}"
|
17
|
-
Ooor.model_registry.set_template(
|
17
|
+
Ooor.model_registry.set_template(session.config, @t)
|
18
18
|
end
|
19
19
|
generate_accessors if fields != {} && (force || !@accessor_defined) #TODOmove in define_accessors method
|
20
20
|
end
|
@@ -30,44 +30,28 @@ module Ooor
|
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
-
# this is used by fields_for in ActionView FormHelper
|
34
|
-
def define_nested_attributes_method(meth)
|
35
|
-
unless self.respond_to?(meth)
|
36
|
-
self.instance_eval do
|
37
|
-
define_method "#{meth}_attributes=" do |*args|
|
38
|
-
send("#{meth}_will_change!")
|
39
|
-
@associations[meth] = args[0]
|
40
|
-
@loaded_associations[meth] = args[0]
|
41
|
-
end
|
42
|
-
define_method "#{meth}_attributes" do |*args|
|
43
|
-
@loaded_associations[meth]
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
33
|
private
|
50
34
|
|
51
35
|
def generate_accessors #TODO we should cache this is a module cached like the template, or eventually generate source code or both
|
52
36
|
fields.keys.each { |meth| define_field_method meth }
|
53
37
|
associations_keys.each { |meth| define_association_method meth }
|
54
|
-
one2many_associations.keys.each { |meth|
|
38
|
+
one2many_associations.keys.each { |meth| accepts_nested_attributes_for meth } #TODO do it for m2o too
|
55
39
|
many2one_associations.keys.each do |meth|
|
56
40
|
define_association_method meth
|
57
41
|
define_m2o_association_method meth
|
58
42
|
end
|
59
43
|
(one2many_associations.keys + many2many_associations.keys).each do |meth|
|
60
44
|
define_association_method meth
|
61
|
-
alias_method "#{meth}_ids", meth
|
62
45
|
alias_method "#{meth}_ids=", "#{meth}="
|
63
|
-
alias_method "#{meth.to_s.singularize}_ids", meth
|
64
46
|
alias_method "#{meth.to_s.singularize}_ids=", "#{meth}="
|
47
|
+
define_x2m_ids_association_method meth
|
48
|
+
alias_method "#{meth.to_s.singularize}_ids", "#{meth}_ids"
|
65
49
|
end
|
66
50
|
@accessor_defined = true
|
67
51
|
end
|
68
52
|
|
69
53
|
def define_field_method(meth)
|
70
|
-
|
54
|
+
define_attribute_method meth
|
71
55
|
define_method meth do |*args|
|
72
56
|
get_attribute(meth, *args)
|
73
57
|
end
|
@@ -78,7 +62,7 @@ module Ooor
|
|
78
62
|
end
|
79
63
|
|
80
64
|
def define_association_method(meth)
|
81
|
-
|
65
|
+
define_attribute_method meth
|
82
66
|
define_method meth do |*args|
|
83
67
|
get_association(meth, *args)
|
84
68
|
end
|
@@ -88,6 +72,12 @@ module Ooor
|
|
88
72
|
end
|
89
73
|
end
|
90
74
|
|
75
|
+
def define_x2m_ids_association_method(meth)
|
76
|
+
define_method "#{meth}_ids" do |*args|
|
77
|
+
@associations[meth]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
91
81
|
def define_m2o_association_method(meth)
|
92
82
|
define_method "#{meth}_id" do |*args|
|
93
83
|
if @associations[meth].is_a? Array
|
@@ -116,12 +106,31 @@ module Ooor
|
|
116
106
|
|
117
107
|
end
|
118
108
|
|
109
|
+
attr_accessor :_display_name
|
110
|
+
alias _name _display_name
|
111
|
+
|
112
|
+
def _destroy=(dummy)
|
113
|
+
@marked_for_destruction = true unless dummy.blank? || ["false", "0", 0].index(dummy)
|
114
|
+
end
|
115
|
+
|
116
|
+
def _destroy
|
117
|
+
@marked_for_destruction
|
118
|
+
end
|
119
|
+
|
120
|
+
def lazy_load(meth, *args)
|
121
|
+
@lazy = false
|
122
|
+
load(rpc_execute('read', [id], (self.class.fast_fields + [meth]).uniq, *args || context)[0]).tap do
|
123
|
+
@lazy = false
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
119
127
|
def get_attribute(meth, *args)
|
128
|
+
lazy_load(meth, *args) if @lazy
|
120
129
|
if @attributes.has_key?(meth)
|
121
130
|
@attributes[meth]
|
122
131
|
else #lazy loading
|
123
132
|
if @attributes["id"]
|
124
|
-
@attributes[meth] = rpc_execute('read', [@attributes["id"]], [meth], *args ||
|
133
|
+
@attributes[meth] = rpc_execute('read', [@attributes["id"]], [meth], *args || context)[0][meth]
|
125
134
|
else
|
126
135
|
nil
|
127
136
|
end
|
@@ -137,16 +146,17 @@ module Ooor
|
|
137
146
|
|
138
147
|
def get_association(meth, *args)
|
139
148
|
return @associations[meth] || :undef if @skip
|
149
|
+
lazy_load(meth, *args) if @lazy
|
140
150
|
if @loaded_associations.has_key?(meth)
|
141
151
|
@loaded_associations[meth]
|
142
152
|
elsif @associations.has_key?(meth)
|
143
153
|
@loaded_associations[meth] = relationnal_result(meth, *args)
|
144
154
|
else
|
145
155
|
if @attributes["id"]
|
146
|
-
@associations[meth] = rpc_execute('read', [@attributes["id"]], [meth], *args ||
|
156
|
+
@associations[meth] = rpc_execute('read', [@attributes["id"]], [meth], *args || context)[0][meth]
|
147
157
|
@loaded_associations[meth] = relationnal_result(meth, *args)
|
148
158
|
elsif self.class.one2many_associations.has_key?(meth) || self.class.many2many_associations.has_key?(meth)
|
149
|
-
load_x2m_association(
|
159
|
+
load_x2m_association(meth, [], *args)
|
150
160
|
else
|
151
161
|
nil
|
152
162
|
end
|
@@ -185,7 +195,7 @@ module Ooor
|
|
185
195
|
# end
|
186
196
|
|
187
197
|
def method_missing(method_symbol, *arguments)
|
188
|
-
self.class.reload_fields_definition(false
|
198
|
+
self.class.reload_fields_definition(false)
|
189
199
|
if id
|
190
200
|
rpc_execute(method_symbol, [id], *arguments) #we assume that's an action
|
191
201
|
else
|