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.
Files changed (99) hide show
  1. data/.gitignore +6 -1
  2. data/Gemfile +11 -0
  3. data/README.md +23 -132
  4. data/Rakefile +0 -8
  5. data/bin/kang +5 -64
  6. data/bin/kangdoc +21 -0
  7. data/bin/kangviz +21 -0
  8. data/config/kangaroo.yml.sample +12 -2
  9. data/docs/Architecture.md +1 -0
  10. data/docs/Classes.md +67 -0
  11. data/docs/Installation.md +2 -1
  12. data/docs/Usage.md +1 -0
  13. data/kangaroo.gemspec +8 -8
  14. data/lib/kangaroo.rb +3 -2
  15. data/lib/kangaroo/commands/base.rb +123 -0
  16. data/lib/kangaroo/commands/cli.rb +65 -0
  17. data/lib/kangaroo/commands/doc.rb +44 -0
  18. data/lib/kangaroo/commands/endpoint.rb +33 -0
  19. data/lib/kangaroo/commands/viz.rb +58 -0
  20. data/lib/kangaroo/doc.rb +2 -0
  21. data/lib/kangaroo/exception.rb +3 -0
  22. data/lib/kangaroo/hirb.rb +2 -1
  23. data/lib/kangaroo/model/associations.rb +33 -0
  24. data/lib/kangaroo/model/associations/many2one.rb +43 -0
  25. data/lib/kangaroo/model/associations/one2many.rb +41 -0
  26. data/lib/kangaroo/model/attributes.rb +88 -93
  27. data/lib/kangaroo/model/base.rb +44 -15
  28. data/lib/kangaroo/model/condition_normalizer.rb +7 -7
  29. data/lib/kangaroo/model/data_import.rb +28 -0
  30. data/lib/kangaroo/model/default_attributes.rb +20 -14
  31. data/lib/kangaroo/model/dynamic_finder.rb +42 -0
  32. data/lib/kangaroo/model/field.rb +52 -1
  33. data/lib/kangaroo/model/field/readonly.rb +39 -0
  34. data/lib/kangaroo/model/finder.rb +6 -2
  35. data/lib/kangaroo/model/inspector.rb +1 -4
  36. data/lib/kangaroo/model/mass_import.rb +42 -0
  37. data/lib/kangaroo/model/open_object_orm.rb +51 -5
  38. data/lib/kangaroo/model/persistence.rb +43 -19
  39. data/lib/kangaroo/model/readonly_attributes.rb +43 -0
  40. data/lib/kangaroo/model/relation.rb +40 -9
  41. data/lib/kangaroo/model/remote_execute.rb +1 -1
  42. data/lib/kangaroo/model/required_attributes.rb +26 -0
  43. data/lib/kangaroo/railtie.rb +13 -3
  44. data/lib/kangaroo/ruby_adapter/base.rb +2 -1
  45. data/lib/kangaroo/ruby_adapter/class_definition.rb +18 -3
  46. data/lib/kangaroo/ruby_adapter/fields.rb +27 -2
  47. data/lib/kangaroo/ruby_adapter/many2one.rb +38 -0
  48. data/lib/kangaroo/ruby_adapter/one2many.rb +31 -0
  49. data/lib/kangaroo/util/client.rb +29 -4
  50. data/lib/kangaroo/util/configuration.rb +15 -10
  51. data/lib/kangaroo/util/database.rb +8 -8
  52. data/lib/kangaroo/util/loader.rb +18 -22
  53. data/lib/kangaroo/util/loader/information_repository.rb +56 -0
  54. data/lib/kangaroo/util/loader/namespace.rb +1 -1
  55. data/lib/kangaroo/util/loader/reflection.rb +61 -0
  56. data/lib/kangaroo/util/loader/root_namespace.rb +12 -21
  57. data/lib/kangaroo/util/proxy.rb +9 -0
  58. data/lib/kangaroo/util/proxy/common.rb +28 -4
  59. data/lib/kangaroo/util/proxy/db.rb +14 -0
  60. data/lib/kangaroo/util/proxy/object.rb +40 -1
  61. data/lib/kangaroo/util/proxy/superadmin.rb +13 -0
  62. data/lib/kangaroo/version.rb +1 -1
  63. data/lib/kangaroo/viz.rb +1 -0
  64. data/lib/kangaroo/viz/base.rb +92 -0
  65. data/spec/commands/base_spec.rb +42 -0
  66. data/spec/functional/associations/many2one_spec.rb +72 -0
  67. data/spec/functional/associations/one2many_spec.rb +65 -0
  68. data/spec/functional/common_service_spec.rb +25 -0
  69. data/spec/functional/data_import_spec.rb +48 -0
  70. data/spec/functional/dynamic_finder_spec.rb +35 -0
  71. data/spec/functional/exception_handling_spec.rb +18 -0
  72. data/spec/functional/identity_spec.rb +48 -0
  73. data/spec/functional/import_export_spec.rb +39 -0
  74. data/spec/functional/lazy_loading_spec.rb +18 -11
  75. data/spec/functional/ordering_spec.rb +33 -0
  76. data/spec/functional/readonly_attributes_spec.rb +37 -0
  77. data/spec/functional/required_attributes_spec.rb +37 -0
  78. data/spec/functional/root_namespace_spec.rb +19 -0
  79. data/spec/functional/select_relation_spec.rb +26 -0
  80. data/spec/model/attributes_spec.rb +1 -0
  81. data/spec/model/base_spec.rb +1 -0
  82. data/spec/model/default_attributes_spec.rb +3 -1
  83. data/spec/model/finder_spec.rb +2 -1
  84. data/spec/model/inspector_spec.rb +1 -0
  85. data/spec/model/open_object_orm_spec.rb +5 -2
  86. data/spec/model/persistence_spec.rb +1 -0
  87. data/spec/model/relation_spec.rb +2 -2
  88. data/spec/ruby_adapter/class_definition_spec.rb +1 -0
  89. data/spec/server_helper.rb +0 -1
  90. data/spec/spec_helper.rb +4 -1
  91. data/spec/util/loader_spec.rb +6 -6
  92. metadata +152 -159
  93. data/Gemfile.lock +0 -69
  94. data/features/configuration.feature +0 -10
  95. data/features/env.rb +0 -8
  96. data/features/step_definitions/basic_steps.rb +0 -18
  97. data/features/step_definitions/configuration_steps.rb +0 -21
  98. data/features/support/test.yml +0 -11
  99. data/features/utility_services.feature +0 -33
@@ -1,21 +1,27 @@
1
1
  module Kangaroo
2
- module DefaultAttributes
3
- # @private
4
- def self.included klass
5
- klass.extend ClassMethods
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
- klass.before_initialize do
8
- return true if persisted?
9
-
10
- self.class.default_attributes.each do |name, value|
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
- module ClassMethods
17
- def default_attributes
18
- default_get :fields => attribute_names
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
@@ -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
@@ -1,10 +1,7 @@
1
1
  module Kangaroo
2
2
  module Model
3
3
  module Inspector
4
- # @private
5
- def self.included klass
6
- klass.extend ClassMethods
7
- end
4
+ extend ActiveSupport::Concern
8
5
 
9
6
  # @private
10
7
  def inspect
@@ -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
- fields = attribute_names if options[:fields].blank?
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 attributes, context
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
- # @private
8
- def self.included klass
9
- klass.extend ClassMethods
10
- klass.define_model_callbacks :destroy, :save, :update, :create, :find
11
- klass.after_destroy :mark_destroyed
12
- klass.after_create :mark_persisted
13
- klass.after_save :reload
14
-
15
- klass.send :attr_accessor, :context
16
-
17
- klass.before_initialize do
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
- !@new_record
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 options ||
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 do |new_record|
96
- new_record.save
97
- end
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(ids) :
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