kangaroo 0.0.3 → 0.1.0.alpha1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +6 -1
- data/Gemfile +11 -0
- data/README.md +23 -132
- data/Rakefile +0 -8
- data/bin/kang +5 -64
- data/bin/kangdoc +21 -0
- data/bin/kangviz +21 -0
- data/config/kangaroo.yml.sample +12 -2
- data/docs/Architecture.md +1 -0
- data/docs/Classes.md +67 -0
- data/docs/Installation.md +2 -1
- data/docs/Usage.md +1 -0
- data/kangaroo.gemspec +8 -8
- data/lib/kangaroo.rb +3 -2
- data/lib/kangaroo/commands/base.rb +123 -0
- data/lib/kangaroo/commands/cli.rb +65 -0
- data/lib/kangaroo/commands/doc.rb +44 -0
- data/lib/kangaroo/commands/endpoint.rb +33 -0
- data/lib/kangaroo/commands/viz.rb +58 -0
- data/lib/kangaroo/doc.rb +2 -0
- data/lib/kangaroo/exception.rb +3 -0
- data/lib/kangaroo/hirb.rb +2 -1
- data/lib/kangaroo/model/associations.rb +33 -0
- data/lib/kangaroo/model/associations/many2one.rb +43 -0
- data/lib/kangaroo/model/associations/one2many.rb +41 -0
- data/lib/kangaroo/model/attributes.rb +88 -93
- data/lib/kangaroo/model/base.rb +44 -15
- data/lib/kangaroo/model/condition_normalizer.rb +7 -7
- data/lib/kangaroo/model/data_import.rb +28 -0
- data/lib/kangaroo/model/default_attributes.rb +20 -14
- data/lib/kangaroo/model/dynamic_finder.rb +42 -0
- data/lib/kangaroo/model/field.rb +52 -1
- data/lib/kangaroo/model/field/readonly.rb +39 -0
- data/lib/kangaroo/model/finder.rb +6 -2
- data/lib/kangaroo/model/inspector.rb +1 -4
- data/lib/kangaroo/model/mass_import.rb +42 -0
- data/lib/kangaroo/model/open_object_orm.rb +51 -5
- data/lib/kangaroo/model/persistence.rb +43 -19
- data/lib/kangaroo/model/readonly_attributes.rb +43 -0
- data/lib/kangaroo/model/relation.rb +40 -9
- data/lib/kangaroo/model/remote_execute.rb +1 -1
- data/lib/kangaroo/model/required_attributes.rb +26 -0
- data/lib/kangaroo/railtie.rb +13 -3
- data/lib/kangaroo/ruby_adapter/base.rb +2 -1
- data/lib/kangaroo/ruby_adapter/class_definition.rb +18 -3
- data/lib/kangaroo/ruby_adapter/fields.rb +27 -2
- data/lib/kangaroo/ruby_adapter/many2one.rb +38 -0
- data/lib/kangaroo/ruby_adapter/one2many.rb +31 -0
- data/lib/kangaroo/util/client.rb +29 -4
- data/lib/kangaroo/util/configuration.rb +15 -10
- data/lib/kangaroo/util/database.rb +8 -8
- data/lib/kangaroo/util/loader.rb +18 -22
- data/lib/kangaroo/util/loader/information_repository.rb +56 -0
- data/lib/kangaroo/util/loader/namespace.rb +1 -1
- data/lib/kangaroo/util/loader/reflection.rb +61 -0
- data/lib/kangaroo/util/loader/root_namespace.rb +12 -21
- data/lib/kangaroo/util/proxy.rb +9 -0
- data/lib/kangaroo/util/proxy/common.rb +28 -4
- data/lib/kangaroo/util/proxy/db.rb +14 -0
- data/lib/kangaroo/util/proxy/object.rb +40 -1
- data/lib/kangaroo/util/proxy/superadmin.rb +13 -0
- data/lib/kangaroo/version.rb +1 -1
- data/lib/kangaroo/viz.rb +1 -0
- data/lib/kangaroo/viz/base.rb +92 -0
- data/spec/commands/base_spec.rb +42 -0
- data/spec/functional/associations/many2one_spec.rb +72 -0
- data/spec/functional/associations/one2many_spec.rb +65 -0
- data/spec/functional/common_service_spec.rb +25 -0
- data/spec/functional/data_import_spec.rb +48 -0
- data/spec/functional/dynamic_finder_spec.rb +35 -0
- data/spec/functional/exception_handling_spec.rb +18 -0
- data/spec/functional/identity_spec.rb +48 -0
- data/spec/functional/import_export_spec.rb +39 -0
- data/spec/functional/lazy_loading_spec.rb +18 -11
- data/spec/functional/ordering_spec.rb +33 -0
- data/spec/functional/readonly_attributes_spec.rb +37 -0
- data/spec/functional/required_attributes_spec.rb +37 -0
- data/spec/functional/root_namespace_spec.rb +19 -0
- data/spec/functional/select_relation_spec.rb +26 -0
- data/spec/model/attributes_spec.rb +1 -0
- data/spec/model/base_spec.rb +1 -0
- data/spec/model/default_attributes_spec.rb +3 -1
- data/spec/model/finder_spec.rb +2 -1
- data/spec/model/inspector_spec.rb +1 -0
- data/spec/model/open_object_orm_spec.rb +5 -2
- data/spec/model/persistence_spec.rb +1 -0
- data/spec/model/relation_spec.rb +2 -2
- data/spec/ruby_adapter/class_definition_spec.rb +1 -0
- data/spec/server_helper.rb +0 -1
- data/spec/spec_helper.rb +4 -1
- data/spec/util/loader_spec.rb +6 -6
- metadata +152 -159
- data/Gemfile.lock +0 -69
- data/features/configuration.feature +0 -10
- data/features/env.rb +0 -8
- data/features/step_definitions/basic_steps.rb +0 -18
- data/features/step_definitions/configuration_steps.rb +0 -21
- data/features/support/test.yml +0 -11
- data/features/utility_services.feature +0 -33
@@ -1,21 +1,27 @@
|
|
1
1
|
module Kangaroo
|
2
|
-
module
|
3
|
-
|
4
|
-
|
5
|
-
|
2
|
+
module Model
|
3
|
+
module DefaultAttributes
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
before_initialize do
|
8
|
+
return true if persisted?
|
9
|
+
|
10
|
+
default_attributes = self.class.default_attributes
|
11
|
+
if default_attributes.blank?
|
12
|
+
return true
|
13
|
+
end
|
6
14
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
write_attribute name, value
|
12
|
-
end
|
15
|
+
default_attributes.each do |name, value|
|
16
|
+
write_attribute name, value
|
17
|
+
end
|
18
|
+
end
|
13
19
|
end
|
14
|
-
end
|
15
20
|
|
16
|
-
|
17
|
-
|
18
|
-
|
21
|
+
module ClassMethods
|
22
|
+
def default_attributes
|
23
|
+
default_get :fields => attribute_names
|
24
|
+
end
|
19
25
|
end
|
20
26
|
end
|
21
27
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Kangaroo
|
2
|
+
module Model
|
3
|
+
module DynamicFinder
|
4
|
+
|
5
|
+
def respond_to? name, *args
|
6
|
+
case name.to_s
|
7
|
+
when /^find_(all|first|last|)_?by_(.+)$/
|
8
|
+
true
|
9
|
+
else
|
10
|
+
super
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
protected
|
15
|
+
def method_missing name, *args
|
16
|
+
name = name.to_s
|
17
|
+
case name
|
18
|
+
when /^find_(all|first|last|)_?by_(.+)$/
|
19
|
+
finder = $1.blank? ? :first : $1
|
20
|
+
fields = $2.split '_and_'
|
21
|
+
define_dynamic_finder name, fields do |relation|
|
22
|
+
relation.send finder
|
23
|
+
end
|
24
|
+
send name, *args
|
25
|
+
else
|
26
|
+
super
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def define_dynamic_finder name, keys
|
31
|
+
singleton_class.send :define_method, name do |*values|
|
32
|
+
conditions = {}
|
33
|
+
keys.each_with_index do |key, i|
|
34
|
+
conditions[key] = values[i]
|
35
|
+
end
|
36
|
+
|
37
|
+
yield where(conditions)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/kangaroo/model/field.rb
CHANGED
@@ -3,14 +3,42 @@ require 'kangaroo/model/attributes'
|
|
3
3
|
module Kangaroo
|
4
4
|
module Model
|
5
5
|
class Field
|
6
|
+
AssociationTypes = %w(many2one one2many one2one many2many)
|
7
|
+
autoload :Readonly, 'kangaroo/model/field/readonly'
|
8
|
+
|
6
9
|
include Attributes
|
10
|
+
include Readonly
|
7
11
|
|
8
12
|
attr_accessor :name
|
9
13
|
define_multiple_accessors :change_default, :context, :digits, :domain, :fnct_inv, :fnct_inv_arg,
|
10
14
|
:fnct_search, :func_method, :func_obj, :function, :help, :invisible,
|
11
15
|
:readonly, :related_columns, :relation, :required, :select, :selectable,
|
12
16
|
:selection, :size, :states, :store, :string, :third_table, :translate,
|
13
|
-
:type
|
17
|
+
:type, :namespace
|
18
|
+
|
19
|
+
def selection?
|
20
|
+
type == 'selection'
|
21
|
+
end
|
22
|
+
|
23
|
+
def char?
|
24
|
+
type == 'char'
|
25
|
+
end
|
26
|
+
|
27
|
+
def float?
|
28
|
+
type == 'float'
|
29
|
+
end
|
30
|
+
|
31
|
+
def functional?
|
32
|
+
!!function
|
33
|
+
end
|
34
|
+
|
35
|
+
def selectable?
|
36
|
+
!!selectable
|
37
|
+
end
|
38
|
+
|
39
|
+
def association?
|
40
|
+
AssociationTypes.include? type
|
41
|
+
end
|
14
42
|
|
15
43
|
def initialize name, attributes = {}
|
16
44
|
@attributes = {}
|
@@ -19,6 +47,29 @@ module Kangaroo
|
|
19
47
|
attributes.each do |key, val|
|
20
48
|
write_attribute key, val
|
21
49
|
end
|
50
|
+
|
51
|
+
self.states = attributes[:states] if attributes[:states]
|
52
|
+
end
|
53
|
+
|
54
|
+
def setter_name
|
55
|
+
"#{name}="
|
56
|
+
end
|
57
|
+
|
58
|
+
def required?
|
59
|
+
!!required
|
60
|
+
end
|
61
|
+
|
62
|
+
def states= states
|
63
|
+
coerced_states = {}
|
64
|
+
|
65
|
+
states.each do |name, effects|
|
66
|
+
coerced_states[name.to_s] = Hash[effects]
|
67
|
+
end
|
68
|
+
write_attribute :states, coerced_states
|
69
|
+
end
|
70
|
+
|
71
|
+
def relation_class
|
72
|
+
@relation_class ||= namespace.class_for relation rescue nil
|
22
73
|
end
|
23
74
|
end
|
24
75
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Kangaroo
|
2
|
+
module Model
|
3
|
+
class Field
|
4
|
+
module Readonly
|
5
|
+
def readonly?
|
6
|
+
!!readonly
|
7
|
+
end
|
8
|
+
|
9
|
+
def eventually_readonly?
|
10
|
+
!!readonly || (states.present? && states.any? { |key, value|
|
11
|
+
!!value['readonly']
|
12
|
+
})
|
13
|
+
end
|
14
|
+
|
15
|
+
def always_readonly?
|
16
|
+
readonly? && (states.blank? || states.all? { |key, value|
|
17
|
+
value['readonly'].nil? || value['readonly'] == true
|
18
|
+
})
|
19
|
+
end
|
20
|
+
|
21
|
+
def readonly_in? state
|
22
|
+
return true if always_readonly?
|
23
|
+
|
24
|
+
s = states && states[state.to_s]
|
25
|
+
|
26
|
+
if readonly?
|
27
|
+
return true unless s
|
28
|
+
|
29
|
+
s['readonly'] == true
|
30
|
+
else
|
31
|
+
return false unless s
|
32
|
+
|
33
|
+
s['readonly'] == false
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -5,6 +5,10 @@ module Kangaroo
|
|
5
5
|
module Finder
|
6
6
|
RELATION_DELEGATES = %w(where limit offset order select context reverse)
|
7
7
|
delegate *(RELATION_DELEGATES + [:to => :relation])
|
8
|
+
|
9
|
+
def find_in_batches batch_size = 100
|
10
|
+
relation.find_in_batches(batch_size)
|
11
|
+
end
|
8
12
|
|
9
13
|
# Retrieve all records
|
10
14
|
#
|
@@ -69,7 +73,7 @@ module Kangaroo
|
|
69
73
|
def search_and_read conditions, search_options = {}, read_options = {}
|
70
74
|
ids = search conditions, search_options.merge(:count => false)
|
71
75
|
return [] if ids.blank?
|
72
|
-
|
76
|
+
|
73
77
|
read ids, read_options
|
74
78
|
end
|
75
79
|
|
@@ -85,7 +89,7 @@ module Kangaroo
|
|
85
89
|
|
86
90
|
# @private
|
87
91
|
def relation
|
88
|
-
Relation.new self
|
92
|
+
@relation ||= Relation.new self
|
89
93
|
end
|
90
94
|
end
|
91
95
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Kangaroo
|
2
|
+
module Model
|
3
|
+
module MassImport
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
module ClassMethods
|
7
|
+
def mass_import objects, options = {}
|
8
|
+
batch_size = options[:batch_size]
|
9
|
+
objects = objects.select { |o| self === o }
|
10
|
+
|
11
|
+
if batch_size
|
12
|
+
until objects.empty?
|
13
|
+
batch_objects = objects.slice! 0, batch_size
|
14
|
+
|
15
|
+
import_data *prepare_mass_import(batch_objects)
|
16
|
+
end
|
17
|
+
else
|
18
|
+
import_data *prepare_mass_import(objects)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
protected
|
23
|
+
def prepare_mass_import objects
|
24
|
+
changed_fields = objects.sum [], &:changed
|
25
|
+
changed_values = []
|
26
|
+
|
27
|
+
changed_fields.uniq!
|
28
|
+
objects.each do |object|
|
29
|
+
attrs = object.instance_variable_get '@attributes'
|
30
|
+
|
31
|
+
values = [object.id] + attrs.values_at(*changed_fields)
|
32
|
+
changed_values << values
|
33
|
+
end
|
34
|
+
|
35
|
+
changed_fields = %w(.id) + changed_fields
|
36
|
+
|
37
|
+
[changed_fields, changed_values]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -38,14 +38,21 @@ module Kangaroo
|
|
38
38
|
# @option options [Hash] context
|
39
39
|
# @return [Array] list of Kangaroo::Model::Base instances
|
40
40
|
def read ids, options = {}
|
41
|
-
fields = options[:fields]
|
42
|
-
|
41
|
+
fields = if options[:fields].present?
|
42
|
+
options[:fields] & attribute_names
|
43
|
+
else
|
44
|
+
attribute_names
|
45
|
+
end
|
46
|
+
|
43
47
|
context = options[:context]
|
48
|
+
ids = ids.reverse if options[:reverse_flag]
|
44
49
|
|
45
50
|
[].tap do |result|
|
46
51
|
remote.read(ids, fields, context).each do |attributes|
|
47
52
|
position = ids.index(attributes[:id].to_i)
|
48
|
-
result[position] = instantiate
|
53
|
+
result[position] = instantiate(attributes, context).tap do |record|
|
54
|
+
record.attribute_names = fields.map &:to_s
|
55
|
+
end
|
49
56
|
end
|
50
57
|
end
|
51
58
|
end
|
@@ -63,7 +70,7 @@ module Kangaroo
|
|
63
70
|
}.merge(options)
|
64
71
|
|
65
72
|
remote.fields_get(options[:fields], options[:context]).map do |key, val|
|
66
|
-
Field.new key, val
|
73
|
+
Field.new key, val.merge(:namespace => namespace)
|
67
74
|
end
|
68
75
|
end
|
69
76
|
|
@@ -86,7 +93,8 @@ module Kangaroo
|
|
86
93
|
# @return [Hash] default values
|
87
94
|
def default_get options = {}
|
88
95
|
options = {
|
89
|
-
:fields => attribute_names
|
96
|
+
:fields => attribute_names,
|
97
|
+
:context => {}
|
90
98
|
}.merge(options)
|
91
99
|
|
92
100
|
remote.default_get options[:fields], options[:context]
|
@@ -112,6 +120,44 @@ module Kangaroo
|
|
112
120
|
def unlink ids, options = {}
|
113
121
|
remote.unlink ids, options[:context]
|
114
122
|
end
|
123
|
+
|
124
|
+
# Get values from information repository for this model
|
125
|
+
# e.g.
|
126
|
+
#
|
127
|
+
# Oo::Product::Product.ir_get 'default'
|
128
|
+
# # => {:supplier_taxes_id => [1], :taxes_id => [2]}
|
129
|
+
#
|
130
|
+
# @param [Symbol] key1
|
131
|
+
# @param [Symbol] key2
|
132
|
+
# @return values
|
133
|
+
def ir_get key1, key2 = false
|
134
|
+
namespace.ir_get key1, key2, self
|
135
|
+
end
|
136
|
+
|
137
|
+
# Export data via OpenERPs export_data method
|
138
|
+
# (which is used by OpenERP to export to CSV)
|
139
|
+
#
|
140
|
+
# @param [Array] ids
|
141
|
+
# @param [Array] fields
|
142
|
+
# @param [Hash] context
|
143
|
+
# @option options [boolean] import_comp Force import compatibility
|
144
|
+
def export_data ids, fields = attribute_names, context = {}
|
145
|
+
remote.export_data(ids, fields, context)[:datas]
|
146
|
+
end
|
147
|
+
|
148
|
+
# Import data via OpenERPs import data method
|
149
|
+
# (which is used by OpenERP to import by CSV)
|
150
|
+
#
|
151
|
+
# @param [Array] fields
|
152
|
+
# @param [Array<Array>] datas
|
153
|
+
# @param [Hash] options
|
154
|
+
# @option options mode "init" or "update", defaults to "init"
|
155
|
+
# @option options current_module
|
156
|
+
# @option options [boolean] noupdate, defaults to false
|
157
|
+
# @option options filename optional file to store partial import state for recovery
|
158
|
+
def import_data fields, datas, options = {}
|
159
|
+
remote.import_data(fields, datas, options)
|
160
|
+
end
|
115
161
|
end
|
116
162
|
end
|
117
163
|
end
|
@@ -4,22 +4,43 @@ require 'active_support/core_ext/hash'
|
|
4
4
|
module Kangaroo
|
5
5
|
module Model
|
6
6
|
module Persistence
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
included do
|
10
|
+
define_model_callbacks :destroy, :save, :update, :create, :find
|
11
|
+
after_destroy :mark_destroyed
|
12
|
+
after_create :mark_persisted
|
13
|
+
after_save :reload
|
14
|
+
|
15
|
+
attr_accessor :context
|
16
|
+
|
17
|
+
before_initialize do
|
18
|
+
@context ||= {}
|
18
19
|
@destroyed = false
|
19
20
|
@readonly = false
|
20
21
|
@new_record = !@id
|
21
22
|
end
|
22
23
|
end
|
24
|
+
|
25
|
+
def update_attribute key, val
|
26
|
+
self.send "#{key}=", val
|
27
|
+
save
|
28
|
+
end
|
29
|
+
|
30
|
+
def update_attribute! key, val
|
31
|
+
self.send "#{key}=", val
|
32
|
+
save!
|
33
|
+
end
|
34
|
+
|
35
|
+
def update_attributes attributes
|
36
|
+
self.attributes = attributes
|
37
|
+
save
|
38
|
+
end
|
39
|
+
|
40
|
+
def update_attributes! attributes
|
41
|
+
self.attributes = attributes
|
42
|
+
save!
|
43
|
+
end
|
23
44
|
|
24
45
|
# Check if this record hasnt been persisted yet
|
25
46
|
#
|
@@ -32,7 +53,7 @@ module Kangaroo
|
|
32
53
|
#
|
33
54
|
# @return [boolean] true/false
|
34
55
|
def persisted?
|
35
|
-
|
56
|
+
!new_record? and !destroyed?
|
36
57
|
end
|
37
58
|
|
38
59
|
# Check if this record has been destroyed
|
@@ -69,7 +90,7 @@ module Kangaroo
|
|
69
90
|
# @param [Hash] options unused
|
70
91
|
# @return [boolean] true
|
71
92
|
def save! options = {}
|
72
|
-
save
|
93
|
+
save(options) ||
|
73
94
|
raise(RecordSavingFailed)
|
74
95
|
end
|
75
96
|
|
@@ -92,9 +113,11 @@ module Kangaroo
|
|
92
113
|
# @param [Hash] attributes
|
93
114
|
# @return saved record
|
94
115
|
def create attributes = {}
|
95
|
-
new(attributes).tap
|
96
|
-
|
97
|
-
|
116
|
+
new(attributes).tap &:save
|
117
|
+
end
|
118
|
+
|
119
|
+
def create! attributes = {}
|
120
|
+
new(attributes).tap &:save!
|
98
121
|
end
|
99
122
|
|
100
123
|
# Retrieve a record by id
|
@@ -104,7 +127,7 @@ module Kangaroo
|
|
104
127
|
# @raise RecordNotFound
|
105
128
|
def find id
|
106
129
|
Array === id ?
|
107
|
-
find_every(
|
130
|
+
find_every(id) :
|
108
131
|
find_single(id)
|
109
132
|
end
|
110
133
|
|
@@ -123,7 +146,8 @@ module Kangaroo
|
|
123
146
|
instance.instance_exec(attributes.stringify_keys, context) do |attributes, context|
|
124
147
|
@attributes = attributes.except 'id'
|
125
148
|
@id = attributes['id']
|
126
|
-
@context = context
|
149
|
+
@context = context || {}
|
150
|
+
|
127
151
|
raise InstantiatedRecordNeedsIDError if @id.nil?
|
128
152
|
|
129
153
|
@new_record = false
|
@@ -139,7 +163,7 @@ module Kangaroo
|
|
139
163
|
|
140
164
|
protected
|
141
165
|
def attributes_for_update
|
142
|
-
@attributes
|
166
|
+
@attributes.slice(*changed)
|
143
167
|
end
|
144
168
|
|
145
169
|
def attributes_for_create
|