ooor 2.0.3 → 2.0.4
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 +7 -0
- data/README.md +1 -0
- data/bin/ooor +1 -0
- data/lib/ooor.rb +2 -0
- data/lib/ooor/associations.rb +30 -40
- data/lib/ooor/base.rb +22 -113
- data/lib/ooor/callbacks.rb +18 -0
- data/lib/ooor/field_methods.rb +133 -85
- data/lib/ooor/helpers/core_helpers.rb +9 -63
- data/lib/ooor/mini_active_resource.rb +0 -18
- data/lib/ooor/naming.rb +2 -2
- data/lib/ooor/persistence.rb +140 -0
- data/lib/ooor/railtie.rb +2 -1
- data/lib/ooor/reflection.rb +11 -399
- data/lib/ooor/reflection_ooor.rb +36 -11
- data/lib/ooor/relation.rb +48 -23
- data/lib/ooor/type_casting.rb +108 -75
- data/lib/ooor/version.rb +1 -1
- data/spec/ooor_spec.rb +133 -27
- metadata +35 -40
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c7013e218cfc09bfb4c0711c65904076f14f6e9d
|
4
|
+
data.tar.gz: 37e409fe1330084c5ada2b5051c92381577febe1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 09af180037d24d0008ef92c4365fa81ca9fbb503b8f07045cb1fd18e134785ddb72b8aff170c9a2c51e136b0422b6e630bef2f534f038702b0821de92254060c
|
7
|
+
data.tar.gz: 2330653e660fd06751bc4bfd1ab79bfe9107b37ae441a2a98bb80ee3a85839046cd14146b4d86fa7bcf9e7d5b5cb5c714b70a7cf34fd08d120a0980d929c847c
|
data/README.md
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
[](http://travis-ci.org/akretion/ooor) [](https://codeclimate.com/github/akretion/ooor)
|
2
|
+
[](https://coveralls.io/r/akretion/ooor?branch=master) [](http://badge.fury.io/rb/ooor) [](https://www.versioneye.com/ruby/ooor)
|
2
3
|
|
3
4
|
[](http://akretion.com)
|
4
5
|
|
data/bin/ooor
CHANGED
data/lib/ooor.rb
CHANGED
data/lib/ooor/associations.rb
CHANGED
@@ -1,63 +1,53 @@
|
|
1
1
|
module Ooor
|
2
2
|
module Associations
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
4
|
+
# similar to ActiveRecord CollectionProxy but without lazy loading work yet
|
5
|
+
class CollectionProxy < Relation
|
6
|
+
|
7
|
+
def to_ary
|
8
|
+
to_a.dup
|
9
|
+
end
|
10
|
+
# alias_method :to_a, :to_ary
|
11
|
+
|
12
|
+
def class
|
13
|
+
Array
|
10
14
|
end
|
11
|
-
end
|
12
15
|
|
13
|
-
|
14
|
-
|
15
|
-
@associations[rel]
|
16
|
-
else
|
17
|
-
method_missing(rel.to_sym, *arguments)
|
16
|
+
def is_a?(*args)
|
17
|
+
@records.is_a?(*args)
|
18
18
|
end
|
19
|
+
|
20
|
+
def kind_of?(*args)
|
21
|
+
@records.kind_of?(*args)
|
22
|
+
end
|
23
|
+
|
19
24
|
end
|
20
25
|
|
26
|
+
|
21
27
|
# fakes associations like much like ActiveRecord according to the cached OpenERP data model
|
22
28
|
def relationnal_result(method_name, *arguments)
|
23
29
|
self.class.reload_fields_definition(false, object_session)
|
24
30
|
if self.class.many2one_associations.has_key?(method_name)
|
25
|
-
if
|
26
|
-
|
27
|
-
id = @associations[method_name].is_a?(Integer) ? @associations[method_name] : @associations[method_name][0]
|
28
|
-
load_association(rel, id, nil, *arguments)
|
31
|
+
if !@associations[method_name]
|
32
|
+
nil
|
29
33
|
else
|
30
|
-
|
34
|
+
id = @associations[method_name].is_a?(Integer) ? @associations[method_name] : @associations[method_name][0]
|
35
|
+
rel = self.class.many2one_associations[method_name]['relation']
|
36
|
+
self.class.const_get(rel).find(id, arguments.extract_options!)
|
31
37
|
end
|
32
|
-
elsif self.class.
|
33
|
-
rel = self.class.one2many_associations[method_name]['relation']
|
34
|
-
load_association(rel, @associations[method_name], [], *arguments)
|
35
|
-
elsif self.class.many2many_associations.has_key?(method_name)
|
36
|
-
rel = self.class.many2many_associations[method_name]['relation']
|
37
|
-
load_association(rel, @associations[method_name], [], *arguments)
|
38
|
-
elsif self.class.polymorphic_m2o_associations.has_key?(method_name)
|
38
|
+
elsif self.class.polymorphic_m2o_associations.has_key?(method_name) && @associations[method_name]
|
39
39
|
values = @associations[method_name].split(',')
|
40
|
-
|
41
|
-
else
|
42
|
-
|
40
|
+
self.class.const_get(values[0]).find(values[1], arguments.extract_options!)
|
41
|
+
else # o2m or m2m
|
42
|
+
rel = self.class.all_fields[method_name]['relation']
|
43
|
+
load_x2m_association(rel, @associations[method_name], *arguments)
|
43
44
|
end
|
44
45
|
end
|
45
46
|
|
46
|
-
def
|
47
|
+
def load_x2m_association(model_key, ids, *arguments)
|
47
48
|
options = arguments.extract_options!
|
48
49
|
related_class = self.class.const_get(model_key)
|
49
|
-
|
50
|
-
context = options[:context] || object_session
|
51
|
-
(related_class.send(:find, ids, fields: fields, context: context) || substitute).tap do |r|
|
52
|
-
#TODO the following is a hack to minimally mimic the CollectionProxy of Rails 3.1+; this should probably be re-implemented
|
53
|
-
def r.association=(association)
|
54
|
-
@association = association
|
55
|
-
end
|
56
|
-
r.association = related_class
|
57
|
-
def r.build(attrs={})
|
58
|
-
@association.new(attrs)
|
59
|
-
end
|
60
|
-
end
|
50
|
+
CollectionProxy.new(related_class, {}).apply_finder_options(options.merge(ids: ids))
|
61
51
|
end
|
62
52
|
|
63
53
|
end
|
data/lib/ooor/base.rb
CHANGED
@@ -1,27 +1,34 @@
|
|
1
1
|
# OOOR: OpenObject On Ruby
|
2
|
-
# Copyright (C) 2009-
|
2
|
+
# Copyright (C) 2009-2014 Akretion LTDA (<http://www.akretion.com>).
|
3
3
|
# Author: Raphaël Valyi
|
4
4
|
# Licensed under the MIT license, see MIT-LICENSE file
|
5
5
|
|
6
|
-
require 'active_support/core_ext/hash/indifferent_access'
|
7
6
|
require 'active_support/core_ext/module/delegation.rb'
|
7
|
+
require 'active_model/attribute_methods'
|
8
|
+
require 'active_model/dirty'
|
8
9
|
require 'ooor/reflection'
|
9
10
|
require 'ooor/reflection_ooor'
|
11
|
+
require 'ooor/errors'
|
10
12
|
|
11
13
|
module Ooor
|
12
|
-
|
13
|
-
|
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,
|
14
20
|
:openerp_model, :field_ids, :state, :fields,
|
15
|
-
:many2one_associations, :one2many_associations, :many2many_associations,
|
16
|
-
:
|
17
|
-
|
18
|
-
end
|
21
|
+
:many2one_associations, :one2many_associations, :many2many_associations,
|
22
|
+
:polymorphic_m2o_associations, :associations_keys,
|
23
|
+
:associations, :columns]
|
19
24
|
|
25
|
+
attr_accessor *TEMPLATE_PROPERTIES, :columns_hash
|
26
|
+
end
|
20
27
|
|
28
|
+
# the base class for proxies to OpenERP objects
|
21
29
|
class Base < Ooor::MiniActiveResource
|
22
|
-
|
23
|
-
include
|
24
|
-
|
30
|
+
include Naming, TypeCasting, Serialization, ReflectionOoor, Reflection
|
31
|
+
include Associations, Report, FinderMethods, FieldMethods
|
25
32
|
|
26
33
|
# ********************** class methods ************************************
|
27
34
|
class << self
|
@@ -71,6 +78,8 @@ module Ooor
|
|
71
78
|
def limit(value); relation.limit(value); end
|
72
79
|
def order(value); relation.order(value); end
|
73
80
|
def offset(value); relation.offset(value); end
|
81
|
+
def first(*args); relation.first(*args); end
|
82
|
+
def last(*args); relation.last(*args); end
|
74
83
|
|
75
84
|
def logger; Ooor.logger; end
|
76
85
|
|
@@ -82,90 +91,13 @@ module Ooor
|
|
82
91
|
|
83
92
|
attr_accessor :associations, :loaded_associations, :ir_model_data_id, :object_session
|
84
93
|
|
94
|
+
include Persistence, Callbacks, ActiveModel::Dirty
|
95
|
+
|
85
96
|
def rpc_execute(method, *args)
|
86
97
|
args += [self.class.connection.connection_session.merge(object_session)] unless args[-1].is_a? Hash
|
87
98
|
self.class.object_service(:execute, self.class.openerp_model, method, *args)
|
88
99
|
end
|
89
100
|
|
90
|
-
def load(attributes, remove_root=false, persisted=false)#an attribute might actually be a association too, will be determined here
|
91
|
-
self.class.reload_fields_definition(false, object_session)
|
92
|
-
raise ArgumentError, "expected an attributes Hash, got #{attributes.inspect}" unless attributes.is_a?(Hash)
|
93
|
-
@prefix_options, attributes = split_options(attributes)
|
94
|
-
@associations ||= {}
|
95
|
-
@attributes ||= {}
|
96
|
-
@loaded_associations = {}
|
97
|
-
attributes.each do |key, value|
|
98
|
-
skey = key.to_s
|
99
|
-
if self.class.associations_keys.index(skey) || value.is_a?(Array) #FIXME may miss m2o with inherits!
|
100
|
-
if value.is_a?(Ooor::Base) || value.is_a?(Array) && value.all? {|i| i.is_a?(Ooor::Base)}
|
101
|
-
@loaded_associations[skey] = value #we want the method to load the association through method missing
|
102
|
-
else
|
103
|
-
@associations[skey] = value
|
104
|
-
end
|
105
|
-
else
|
106
|
-
@attributes[skey] = value || nil #don't bloat with false values
|
107
|
-
end
|
108
|
-
end
|
109
|
-
self
|
110
|
-
end
|
111
|
-
|
112
|
-
#takes care of reading OpenERP default field values.
|
113
|
-
def initialize(attributes = {}, default_get_list=false, context={}, persisted=false)
|
114
|
-
@attributes = {}
|
115
|
-
@prefix_options = {}
|
116
|
-
@ir_model_data_id = attributes.delete(:ir_model_data_id)
|
117
|
-
@object_session = {}
|
118
|
-
@object_session = HashWithIndifferentAccess.new(context)
|
119
|
-
@persisted = persisted
|
120
|
-
self.class.reload_fields_definition(false, @object_session)
|
121
|
-
if default_get_list == []
|
122
|
-
load(attributes)
|
123
|
-
else
|
124
|
-
load_with_defaults(attributes, default_get_list)
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
def save(context={}, reload=true)
|
129
|
-
new? ? create(context, reload) : update(context, reload)
|
130
|
-
rescue ValidationError => e
|
131
|
-
e.extract_validation_error!(errors)
|
132
|
-
return false
|
133
|
-
end
|
134
|
-
|
135
|
-
#compatible with the Rails way but also supports OpenERP context
|
136
|
-
def create(context={}, reload=true)
|
137
|
-
self.id = rpc_execute('create', to_openerp_hash, context)
|
138
|
-
if @ir_model_data_id
|
139
|
-
IrModelData.create(model: self.class.openerp_model,
|
140
|
-
'module' => @ir_model_data_id[0],
|
141
|
-
'name' => @ir_model_data_id[1],
|
142
|
-
'res_id' => self.id)
|
143
|
-
end
|
144
|
-
reload_from_record!(self.class.find(self.id, context: context)) if reload
|
145
|
-
@persisted = true
|
146
|
-
end
|
147
|
-
|
148
|
-
def update_attributes(attributes, context={}, reload=true)
|
149
|
-
load(attributes, false) && save(context, reload)
|
150
|
-
end
|
151
|
-
|
152
|
-
#compatible with the Rails way but also supports OpenERP context
|
153
|
-
def update(context={}, reload=true) #TODO use http://apidock.com/rails/ActiveRecord/Dirty to minimize data to save back
|
154
|
-
rpc_execute('write', [self.id], to_openerp_hash, context)
|
155
|
-
reload_fields(context) if reload
|
156
|
-
@persisted = true
|
157
|
-
end
|
158
|
-
|
159
|
-
#compatible with the Rails way but also supports OpenERP context
|
160
|
-
def destroy(context={})
|
161
|
-
rpc_execute('unlink', [self.id], context)
|
162
|
-
end
|
163
|
-
|
164
|
-
#OpenERP copy method, load persisted copied Object
|
165
|
-
def copy(defaults={}, context={})
|
166
|
-
self.class.find(rpc_execute('copy', self.id, defaults, context), context: context)
|
167
|
-
end
|
168
|
-
|
169
101
|
#Generic OpenERP rpc method call
|
170
102
|
def call(method, *args) rpc_execute(method, *args) end
|
171
103
|
|
@@ -192,31 +124,8 @@ module Ooor
|
|
192
124
|
|
193
125
|
private
|
194
126
|
|
195
|
-
def load_with_defaults(attributes, default_get_list)
|
196
|
-
defaults = rpc_execute("default_get", default_get_list || self.class.fields.keys + self.class.associations_keys, object_session.dup)
|
197
|
-
attributes = HashWithIndifferentAccess.new(defaults.merge(attributes.reject {|k, v| v.blank? }))
|
198
|
-
load(attributes)
|
199
|
-
end
|
200
|
-
|
201
|
-
def load_on_change_result(result, field_name, field_value)
|
202
|
-
if result["warning"]
|
203
|
-
self.class.logger.info result["warning"]["title"]
|
204
|
-
self.class.logger.info result["warning"]["message"]
|
205
|
-
end
|
206
|
-
attrs = @attributes.merge(field_name => field_value)
|
207
|
-
attrs.merge!(result["value"])
|
208
|
-
load(attrs)
|
209
|
-
end
|
210
|
-
|
211
127
|
# Ruby 1.9.compat, See also http://tenderlovemaking.com/2011/06/28/til-its-ok-to-return-nil-from-to_ary/
|
212
128
|
def to_ary; nil; end # :nodoc:
|
213
129
|
|
214
|
-
def reload_from_record!(record) load(record.attributes.merge(record.associations)) end
|
215
|
-
|
216
|
-
def reload_fields(context)
|
217
|
-
records = self.class.find(self.id, context: context, fields: @attributes.keys + @associations.keys)
|
218
|
-
reload_from_record!(records)
|
219
|
-
end
|
220
|
-
|
221
130
|
end
|
222
131
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Ooor
|
2
|
+
module Callbacks
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
CALLBACKS = [
|
6
|
+
:before_validation, :after_validation, :before_save, :around_save, :after_save,
|
7
|
+
:before_create, :around_create, :after_create, :before_update, :around_update,
|
8
|
+
:after_update, :before_destroy, :around_destroy, :after_destroy
|
9
|
+
]
|
10
|
+
|
11
|
+
included do
|
12
|
+
extend ActiveModel::Callbacks
|
13
|
+
include ActiveModel::Validations::Callbacks
|
14
|
+
|
15
|
+
define_model_callbacks :save, :create, :update, :destroy
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/ooor/field_methods.rb
CHANGED
@@ -13,22 +13,17 @@ module Ooor
|
|
13
13
|
fields_get = rpc_execute("fields_get", false, context)
|
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
|
-
(fields.keys + associations_keys).each do |meth| #generates method handlers for auto-completion tools
|
17
|
-
define_field_method(meth)
|
18
|
-
end
|
19
|
-
one2many_associations.keys.each do |meth|
|
20
|
-
define_nested_attributes_method(meth)
|
21
|
-
end
|
22
16
|
logger.debug "#{fields.size} fields loaded in model #{self.name}"
|
23
17
|
Ooor.model_registry.set_template(connection.config, @t)
|
24
18
|
end
|
19
|
+
generate_accessors if fields != {} && (force || !@accessor_defined) #TODOmove in define_accessors method
|
25
20
|
end
|
26
21
|
|
27
22
|
def all_fields
|
28
23
|
fields.merge(polymorphic_m2o_associations).merge(many2many_associations).merge(one2many_associations).merge(many2one_associations)
|
29
24
|
end
|
30
25
|
|
31
|
-
def fast_fields(options)
|
26
|
+
def fast_fields(options={})
|
32
27
|
fields = all_fields
|
33
28
|
fields.keys.select do |k|
|
34
29
|
fields[k]["type"] != "binary" && (options[:include_functions] || !fields[k]["function"])
|
@@ -40,10 +35,12 @@ module Ooor
|
|
40
35
|
unless self.respond_to?(meth)
|
41
36
|
self.instance_eval do
|
42
37
|
define_method "#{meth}_attributes=" do |*args|
|
43
|
-
|
38
|
+
send("#{meth}_will_change!")
|
39
|
+
@associations[meth] = args[0]
|
40
|
+
@loaded_associations[meth] = args[0]
|
44
41
|
end
|
45
42
|
define_method "#{meth}_attributes" do |*args|
|
46
|
-
|
43
|
+
@loaded_associations[meth]
|
47
44
|
end
|
48
45
|
end
|
49
46
|
end
|
@@ -51,102 +48,153 @@ module Ooor
|
|
51
48
|
|
52
49
|
private
|
53
50
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
end
|
51
|
+
def generate_accessors #TODO we should cache this is a module cached like the template, or eventually generate source code or both
|
52
|
+
fields.keys.each { |meth| define_field_method meth }
|
53
|
+
associations_keys.each { |meth| define_association_method meth }
|
54
|
+
one2many_associations.keys.each { |meth| define_nested_attributes_method meth }
|
55
|
+
many2one_associations.keys.each do |meth|
|
56
|
+
define_association_method meth
|
57
|
+
define_m2o_association_method meth
|
62
58
|
end
|
59
|
+
(one2many_associations.keys + many2many_associations.keys).each do |meth|
|
60
|
+
define_association_method meth
|
61
|
+
alias_method "#{meth}_ids", meth
|
62
|
+
alias_method "#{meth}_ids=", "#{meth}="
|
63
|
+
alias_method "#{meth.to_s.singularize}_ids", meth
|
64
|
+
alias_method "#{meth.to_s.singularize}_ids=", "#{meth}="
|
65
|
+
end
|
66
|
+
@accessor_defined = true
|
67
|
+
end
|
63
68
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
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
|
-
fields[k] = field if field['name'] != 'id'
|
76
|
-
end
|
69
|
+
def define_field_method(meth)
|
70
|
+
define_attribute_methods meth
|
71
|
+
define_method meth do |*args|
|
72
|
+
get_attribute(meth, *args)
|
77
73
|
end
|
78
|
-
end
|
79
74
|
|
80
|
-
|
81
|
-
|
82
|
-
method_key = method_name.sub('=', '')
|
83
|
-
self.class.reload_fields_definition(false, object_session)
|
84
|
-
if attributes.has_key?(method_key)
|
85
|
-
if method_name.end_with?('=')
|
86
|
-
attributes[method_key] = arguments[0]
|
87
|
-
else
|
88
|
-
attributes[method_key]
|
75
|
+
define_method "#{meth}=" do |*args|
|
76
|
+
set_attribute(meth, *args)
|
89
77
|
end
|
90
|
-
elsif @loaded_associations.has_key?(method_name)
|
91
|
-
@loaded_associations[method_name]
|
92
|
-
elsif @associations.has_key?(method_name)
|
93
|
-
result = relationnal_result(method_name, *arguments)
|
94
|
-
@loaded_associations[method_name] = result and return result if result
|
95
|
-
elsif method_name.end_with?('=')
|
96
|
-
return method_missing_value_assign(method_key, arguments)
|
97
|
-
elsif self.class.fields.has_key?(method_name) || self.class.associations_keys.index(method_name) #unloaded field/association
|
98
|
-
return lazzy_load_field(method_name, *arguments)
|
99
|
-
# check if that is not a Rails style association with an _id[s][=] suffix:
|
100
|
-
elsif method_name.match(/_id$/) && self.class.associations_keys.index(rel=method_name.gsub(/_id$/, ""))
|
101
|
-
return many2one_id_method(rel, *arguments)
|
102
|
-
elsif method_name.match(/_ids$/) && self.class.associations_keys.index(rel=method_name.gsub(/_ids$/, ""))
|
103
|
-
return x_to_many_ids_method(rel, *arguments)
|
104
|
-
elsif id
|
105
|
-
rpc_execute(method_key, [id], *arguments) #we assume that's an action
|
106
|
-
else
|
107
|
-
super
|
108
78
|
end
|
109
79
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
80
|
+
def define_association_method(meth)
|
81
|
+
define_attribute_methods meth
|
82
|
+
define_method meth do |*args|
|
83
|
+
get_association(meth, *args)
|
84
|
+
end
|
114
85
|
|
115
|
-
|
86
|
+
define_method "#{meth}=" do |*args|
|
87
|
+
set_association(meth, *args)
|
88
|
+
end
|
89
|
+
end
|
116
90
|
|
117
|
-
def
|
118
|
-
|
119
|
-
@associations[
|
120
|
-
|
121
|
-
|
122
|
-
|
91
|
+
def define_m2o_association_method(meth)
|
92
|
+
define_method "#{meth}_id" do |*args|
|
93
|
+
if @associations[meth].is_a? Array
|
94
|
+
@associations[meth][0]
|
95
|
+
else
|
96
|
+
r = get_association(meth, *args)
|
97
|
+
r.is_a?(Ooor::Base) ? r.id : r
|
98
|
+
end
|
123
99
|
end
|
124
100
|
end
|
125
101
|
|
126
|
-
def
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
102
|
+
def reload_field_definition(k, field)
|
103
|
+
case field['type']
|
104
|
+
when 'many2one'
|
105
|
+
many2one_associations[k] = field
|
106
|
+
when 'one2many'
|
107
|
+
one2many_associations[k] = field
|
108
|
+
when 'many2many'
|
109
|
+
many2many_associations[k] = field
|
110
|
+
when 'reference'
|
111
|
+
polymorphic_m2o_associations[k] = field
|
112
|
+
else
|
113
|
+
fields[k] = field if field['name'] != 'id'
|
114
|
+
end
|
132
115
|
end
|
133
116
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
117
|
+
end
|
118
|
+
|
119
|
+
def get_attribute(meth, *args)
|
120
|
+
if @attributes.has_key?(meth)
|
121
|
+
@attributes[meth]
|
122
|
+
else #lazy loading
|
123
|
+
if @attributes["id"]
|
124
|
+
@attributes[meth] = rpc_execute('read', [@attributes["id"]], [meth], *args || object_session)[0][meth]
|
125
|
+
else
|
126
|
+
nil
|
127
|
+
end
|
140
128
|
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def set_attribute(meth, *args)
|
132
|
+
value = sanitize_attribute(meth, args[0])
|
133
|
+
@attributes[meth] ||= nil
|
134
|
+
send("#{meth}_will_change!") unless @attributes[meth] == value
|
135
|
+
@attributes[meth] = value
|
136
|
+
end
|
141
137
|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
138
|
+
def get_association(meth, *args)
|
139
|
+
return @associations[meth] || :undef if @skip
|
140
|
+
if @loaded_associations.has_key?(meth)
|
141
|
+
@loaded_associations[meth]
|
142
|
+
elsif @associations.has_key?(meth)
|
143
|
+
@loaded_associations[meth] = relationnal_result(meth, *args)
|
144
|
+
else
|
145
|
+
if @attributes["id"]
|
146
|
+
@associations[meth] = rpc_execute('read', [@attributes["id"]], [meth], *args || object_session)[0][meth]
|
147
|
+
@loaded_associations[meth] = relationnal_result(meth, *args)
|
148
|
+
elsif self.class.one2many_associations.has_key?(meth) || self.class.many2many_associations.has_key?(meth)
|
149
|
+
load_x2m_association(self.class.all_fields[meth]['relation'], [], *args)
|
146
150
|
else
|
147
151
|
nil
|
148
152
|
end
|
149
153
|
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def set_association(meth, *args)
|
157
|
+
value = sanitize_association(meth, args[0])
|
158
|
+
if self.class.many2one_associations.has_key?(meth) # TODO detect false positives changes for other associations too
|
159
|
+
if @associations[meth].is_a?(Array) && @associations[meth][0] == value \
|
160
|
+
|| @associations[meth] == value #\
|
161
|
+
return value
|
162
|
+
end
|
163
|
+
end
|
164
|
+
@skip = true
|
165
|
+
send("#{meth}_will_change!")
|
166
|
+
@skip = false
|
167
|
+
if value.is_a?(Ooor::Base) || value.is_a?(Array) && !value.empty? && value.all? {|i| i.is_a?(Ooor::Base)}
|
168
|
+
@loaded_associations[meth] = value
|
169
|
+
else
|
170
|
+
@loaded_associations.delete(meth)
|
171
|
+
end
|
172
|
+
@associations[meth] = value
|
173
|
+
end
|
174
|
+
|
175
|
+
# # Raise NoMethodError if the named attribute does not exist in order to preserve behavior expected by #clone.
|
176
|
+
# def attribute(name)
|
177
|
+
# key = name.to_s
|
178
|
+
# if self.class.fields.has_key?(key) #TODO check not symbols
|
179
|
+
# get_attribute(key)
|
180
|
+
# elsif self.class.associations_keys.index(key)
|
181
|
+
# get_association(key)
|
182
|
+
# else
|
183
|
+
# raise NoMethodError
|
184
|
+
# end
|
185
|
+
# end
|
186
|
+
|
187
|
+
def method_missing(method_symbol, *arguments)
|
188
|
+
self.class.reload_fields_definition(false, object_session)
|
189
|
+
if id
|
190
|
+
rpc_execute(method_symbol, [id], *arguments) #we assume that's an action
|
191
|
+
else
|
192
|
+
super
|
193
|
+
end
|
194
|
+
rescue UnknownAttributeOrAssociationError => e
|
195
|
+
e.klass = self.class
|
196
|
+
raise e
|
197
|
+
end
|
150
198
|
|
151
199
|
end
|
152
200
|
end
|