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
@@ -0,0 +1,43 @@
1
+ module Kangaroo
2
+ module Model
3
+ module ReadonlyAttributes
4
+
5
+ def define_setter attribute_name
6
+ field = fields_hash[attribute_name.to_sym] rescue nil
7
+
8
+ super and return unless field
9
+ return if field.always_readonly?
10
+
11
+ unless field.eventually_readonly?
12
+ super
13
+ else
14
+ define_method "#{attribute_name}=" do |value|
15
+ if state.blank? && field.readonly?
16
+ raise Kangaroo::ReadonlyRecord
17
+ elsif field.readonly_in? state
18
+ raise Kangaroo::ReadonlyRecord
19
+ else
20
+ write_attribute attribute_name, value
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+
27
+ protected
28
+ def attributes_for_update
29
+ without_readonly_attributes super
30
+ end
31
+
32
+ def attributes_for_create
33
+ without_readonly_attributes super
34
+ end
35
+
36
+ def without_readonly_attributes attributes
37
+ attributes.reject do |key, val|
38
+ state && fields_hash[key.to_sym].readonly_in?(state)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -1,6 +1,10 @@
1
+ require 'kangaroo/model/dynamic_finder'
2
+
1
3
  module Kangaroo
2
4
  module Model
3
5
  class Relation
6
+ include DynamicFinder
7
+
4
8
  # @private
5
9
  ARRAY_DELEGATES = %w( all? any? as_json at b64encode blank? choice class clone collect collect! combination compact compact! concat
6
10
  cycle decode64 delete delete_at delete_if detect drop drop_while dup duplicable? each each_cons each_index
@@ -17,7 +21,14 @@ module Kangaroo
17
21
  type uniq uniq! uniq_by uniq_by! unshift values_at yaml_initialize zip |).freeze
18
22
 
19
23
  # @private
20
- attr_accessor :target, :where_clauses, :offset_clause, :limit_clause, :select_clause, :order_clause, :context_clause
24
+ attr_accessor :target,
25
+ :where_clauses,
26
+ :offset_clause,
27
+ :limit_clause,
28
+ :select_clause,
29
+ :order_clause,
30
+ :context_clause,
31
+ :reverse_flag
21
32
 
22
33
  alias_method :_clone, :clone
23
34
  alias_method :_tap, :tap
@@ -46,7 +57,8 @@ module Kangaroo
46
57
 
47
58
  # Return only the last record
48
59
  def last
49
- reverse.first
60
+ count = self.count
61
+ offset(count - 1).limit(1).first
50
62
  end
51
63
 
52
64
  # Check if a record with fulfilling this conditions exist
@@ -62,6 +74,18 @@ module Kangaroo
62
74
  records.all :
63
75
  records.first
64
76
  end
77
+
78
+ def find_in_batches batch_size = 100
79
+ objects_count = count
80
+ batches_count = objects_count / batch_size + 1
81
+
82
+ objects = []
83
+ batches_count.times do |batch_no|
84
+ objects += limit(batch_size).offset(batch_no * batch_size).all
85
+ end
86
+
87
+ objects
88
+ end
65
89
 
66
90
  # Count how many records fulfill this conditions
67
91
  def count
@@ -73,9 +97,12 @@ module Kangaroo
73
97
  # Reverse all order clauses
74
98
  def reverse
75
99
  if @order_clause.blank?
76
- order('id', true)
100
+ _clone._tap do |c|
101
+ c.reverse_flag = !c.reverse_flag
102
+ end
77
103
  else
78
104
  _clone._tap do |c|
105
+ c.reverse_flag = false
79
106
  c.order_clause = c.order_clause.map do |order|
80
107
  reverse_order order
81
108
  end
@@ -139,8 +166,11 @@ module Kangaroo
139
166
  # @param [String, Symbol] column field to order by
140
167
  # @param [boolean] desc true to order descending
141
168
  def order column, desc = false
142
- column = column.to_s + " desc" if desc
169
+ column = column.to_s
170
+ column = column + " desc" if desc
171
+
143
172
  _clone._tap do |c|
173
+ c.reverse_flag = true if column.downcase == 'id desc'
144
174
  c.order_clause += [column.to_s]
145
175
  end
146
176
  end
@@ -186,17 +216,18 @@ module Kangaroo
186
216
  protected
187
217
  def search_parameters
188
218
  {
189
- :offset => @offset_clause,
190
- :limit => @limit_clause,
191
- :order => @order_clause.join(", "),
219
+ :offset => @offset_clause,
220
+ :limit => @limit_clause,
221
+ :order => @order_clause.join(", "),
192
222
  :context => @context_clause
193
223
  }
194
224
  end
195
225
 
196
226
  def read_parameters
197
227
  {
198
- :fields => @select_clause,
199
- :context => @context_clause
228
+ :fields => @select_clause,
229
+ :context => @context_clause,
230
+ :reverse_flag => @reverse_flag
200
231
  }
201
232
  end
202
233
 
@@ -2,7 +2,7 @@ module Kangaroo
2
2
  module Model
3
3
  module RemoteExecute
4
4
  def call name, *args
5
- return_value = remote.send name, ids_for_execute, *args
5
+ return_value = remote.call! name, ids_for_execute, *args
6
6
 
7
7
  # TODO: handle warnings etc
8
8
  if Hash === return_value && return_value[:value]
@@ -0,0 +1,26 @@
1
+ module Kangaroo
2
+ module Model
3
+ module RequiredAttributes
4
+ extend ActiveSupport::Concern
5
+ include ActiveModel::Validations
6
+
7
+ def save options = {}
8
+ if options[:validate] != false
9
+ valid? && super
10
+ else
11
+ super
12
+ end
13
+ end
14
+
15
+ module ClassMethods
16
+ def define_setter attribute_name
17
+ if fields_hash[attribute_name.to_sym].try :required?
18
+ validates_presence_of attribute_name
19
+ end
20
+
21
+ super
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -1,13 +1,23 @@
1
1
  module Kangaroo
2
2
  class Railtie < Rails::Railtie
3
- initializer 'kangaroo.initialize' do
3
+ attr_accessor :configuration
4
+
5
+ # Get the client instance configured via config/kangaroo.yml
6
+ #
7
+ # @return [Kangaroo::Util::Client] client
8
+ def client
9
+ @configuration.try :client
10
+ end
11
+
12
+ config.to_prepare do
4
13
  begin
5
14
  config_file = File.join(Rails.root, %w(config kangaroo.yml))
6
- configuration = Kangaroo::Util::Configuration.new config_file, Rails.logger
7
- configuration.load_models
15
+ @configuration = Kangaroo::Util::Configuration.new config_file, Rails.logger
16
+ @configuration.load_models
8
17
  rescue Errno::ECONNREFUSED => e
9
18
  Rails.logger.error "Could not connect to OpenERP XML-RPC Service."
10
19
  end
11
20
  end
21
+
12
22
  end
13
23
  end
@@ -20,7 +20,8 @@ module Kangaroo
20
20
  def to_ruby
21
21
  define_class
22
22
  add_fields
23
-
23
+ add_associations
24
+
24
25
  @ruby_model
25
26
  end
26
27
  end
@@ -15,12 +15,14 @@ module Kangaroo
15
15
 
16
16
  def define_model_class
17
17
  @ruby_model = set_const_in @namespace, constant_names.last, model_subclass
18
-
18
+
19
19
  if !@ruby_model.is_a?(Class)
20
20
  raise ChildDefinedBeforeParentError
21
21
  end
22
22
  @ruby_model.database = @oo_model.class.database
23
-
23
+ @ruby_model.namespace = @oo_model.class.namespace
24
+ @ruby_model.oo_name = @oo_model.model
25
+ @ruby_model.oo_model = @oo_model
24
26
  @ruby_model
25
27
  end
26
28
 
@@ -40,9 +42,22 @@ module Kangaroo
40
42
 
41
43
  # Set constant only if not already defined
42
44
  def set_const_in mod, name, const
43
- mod.const_set name, const unless mod.const_defined?(name)
45
+ mod.const_set name, const unless const_already_defined?(mod, name)
44
46
  mod.const_get name
45
47
  end
48
+
49
+ def const_already_defined? mod, name
50
+ if is_18?
51
+ mod.const_defined? name
52
+ else
53
+ mod.const_defined? name, false
54
+ end
55
+ end
56
+
57
+ protected
58
+ def is_18?
59
+ RUBY_VERSION =~ /^1\.8/
60
+ end
46
61
  end
47
62
  end
48
63
  end
@@ -1,13 +1,38 @@
1
+ require 'kangaroo/ruby_adapter/many2one'
2
+ require 'kangaroo/ruby_adapter/one2many'
3
+
1
4
  module Kangaroo
2
5
  module RubyAdapter
3
6
  module Fields
7
+ include Many2one
8
+ include One2many
9
+
4
10
  def add_fields
5
11
  @ruby_model.define_multiple_accessors *field_names
6
12
  end
7
13
 
8
- protected
14
+ def add_associations
15
+ @ruby_model.association_fields.each do |association_field|
16
+ add_association association_field
17
+ end
18
+ end
19
+
20
+ def add_association association_field
21
+ case association_field.type
22
+ when 'many2one'
23
+ add_many2one_association association_field
24
+ when 'one2many'
25
+ add_one2many_association association_field
26
+ end
27
+ end
28
+
29
+ protected
30
+ def define_method_in_model name, &block
31
+ @ruby_model.send :define_method, name, &block
32
+ end
33
+
9
34
  def field_names
10
- fields.map &:name
35
+ fields.map(&:name) - [:id]
11
36
  end
12
37
 
13
38
  def fields
@@ -0,0 +1,38 @@
1
+ module Kangaroo
2
+ module RubyAdapter
3
+ module Many2one
4
+ protected
5
+ def add_many2one_association association_field
6
+ define_many2one_id_accessor association_field
7
+ define_many2one_name_accessor association_field
8
+ define_many2one_obj_accessor association_field
9
+ end
10
+
11
+ def define_many2one_name_accessor association_field
12
+ define_method_in_model "#{association_field.name}_name" do
13
+ read_many2one_name_for association_field
14
+ end
15
+ end
16
+
17
+ def define_many2one_id_accessor association_field
18
+ define_method_in_model "#{association_field.name}_id" do
19
+ read_many2one_id_for association_field
20
+ end
21
+
22
+ define_method_in_model "#{association_field.name}_id=" do |id|
23
+ write_many2one_id_for association_field, id
24
+ end
25
+ end
26
+
27
+ def define_many2one_obj_accessor association_field
28
+ define_method_in_model "#{association_field.name}_obj" do
29
+ read_many2one_obj_for association_field
30
+ end
31
+
32
+ define_method_in_model "#{association_field.name}_obj=" do |obj|
33
+ write_many2one_obj_for association_field, obj
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,31 @@
1
+ module Kangaroo
2
+ module RubyAdapter
3
+ module One2many
4
+ protected
5
+ def add_one2many_association association_field
6
+ define_one2many_id_accessor association_field
7
+ define_one2many_obj_accessor association_field
8
+ end
9
+
10
+ def define_one2many_id_accessor association_field
11
+ define_method_in_model "#{association_field.name}_ids" do
12
+ read_one2many_ids_for association_field
13
+ end
14
+
15
+ define_method_in_model "#{association_field.name}_ids=" do |ids|
16
+ write_one2many_ids_for association_field, ids
17
+ end
18
+ end
19
+
20
+ def define_one2many_obj_accessor association_field
21
+ define_method_in_model "#{association_field.name}_objs" do
22
+ read_one2many_objs_for association_field
23
+ end
24
+
25
+ define_method_in_model "#{association_field.name}_objs=" do |objs|
26
+ write_one2many_objs_for association_field, objs
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -4,6 +4,31 @@ require 'kangaroo/util/proxy'
4
4
 
5
5
  module Kangaroo
6
6
  module Util
7
+ # This is the central point to interact with OpenERP. You can configure your
8
+ # connection via Kangaroo::Util::Configuration.
9
+ #
10
+ # @see Kangaroo::Util::Configuration
11
+ # @see Kangaroo::Util::Database
12
+ # @example Configure Kangaroo and get the client instance
13
+ # config = Kangaroo::Util::Configuration.new 'spec/test_env/test.yml'
14
+ # client = config.client
15
+ #
16
+ # @example Use non-database dependent services
17
+ # client.common
18
+ # client.db
19
+ # client.superadmin 'superadminpassword'
20
+ #
21
+ # @example Use database dependent services
22
+ # client.database.object 'product.product'
23
+ # client.database.wizard
24
+ # client.database.report
25
+ # client.database.workflow
26
+ #
27
+ # @example For convenience, the non-database dependent services are also available via database
28
+ # client.database.common
29
+ # client.database.db
30
+ # client.database.superadmin 'superadminpassword'
31
+ #
7
32
  class Client < Rapuncel::Client
8
33
  SERVICES = %w(db common object wizard report).freeze
9
34
 
@@ -33,7 +58,7 @@ module Kangaroo
33
58
  RUBY
34
59
  end
35
60
 
36
- # Access the {Kangaroo::Util::Proxy::Superadmin Superadmin Proxy}
61
+ # Access the Kangaroo::Util::Proxy::Superadmin
37
62
  #
38
63
  # @param [String] super_password Superadmin password
39
64
  # @return [Kangaroo::Util::Proxy::Superadmin] Superadmin proxy
@@ -41,14 +66,14 @@ module Kangaroo
41
66
  Proxy::Superadmin.new db_service, super_password
42
67
  end
43
68
 
44
- # Access the {Kangaroo::Util::Proxy::Common Common Proxy}
69
+ # Access the Kangaroo::Util::Proxy::Common
45
70
  #
46
71
  # @return [Kangaroo::Util::Proxy::Common] Common proxy
47
72
  def common
48
73
  @common_proxy ||= Proxy::Common.new common_service
49
74
  end
50
75
 
51
- # Access the {Kangaroo::Util::Proxy::Db Db Proxy}
76
+ # Access the Kangaroo::Util::Proxy::Db
52
77
  #
53
78
  # @return [Kangaroo::Util::Proxy::Db] Db proxy
54
79
  def db
@@ -56,7 +81,7 @@ module Kangaroo
56
81
  end
57
82
 
58
83
  def inspect
59
- "#Kangaroo::Util::Client:0x#{hash.to_s(16)}>"
84
+ "<#Kangaroo::Util::Client:0x#{hash.to_s(16)}>"
60
85
  end
61
86
  end
62
87
  end