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,5 +1,20 @@
1
1
  module Kangaroo
2
2
  module Util
3
+ # Proxy to the object service (at /xmlrpc/object), which provides everything
4
+ # to read, create and modify OpenERP objects.
5
+ #
6
+ # @example Configure Kangaroo and get the database instance
7
+ # config = Kangaroo::Util::Configuration.new 'spec/test_env/test.yml'
8
+ # client = config.client
9
+ # database = client.database
10
+ #
11
+ # @example
12
+ # database.object('product.product').fields_get ['name', 'description']
13
+ #
14
+ # @note OpenERPs object service actually only exposes the method 'execute', which needs the
15
+ # ORM method to call (e.g. fields_get) as first argument, and then the model to operate on.
16
+ # Proxy::Object already takes care of that, so that you can call ORM methods directly. (see example)
17
+ #
3
18
  class Proxy::Object < Proxy
4
19
  # Call function via execute on OpenERPs object service.
5
20
  #
@@ -115,7 +130,7 @@ module Kangaroo
115
130
  # @param [Hash] context
116
131
  # @return [Array] Array of Hashes with field names and values
117
132
  def read ids, fields = [], context = nil
118
- call! :read, ids, fields
133
+ call! :read, ids, fields, context
119
134
  end
120
135
 
121
136
  # Update records
@@ -147,6 +162,30 @@ module Kangaroo
147
162
  def read_group domain, fields, groupby, offset = 0, limit = nil, order = nil
148
163
  call! :read_group, domain, fields, groupby, offset, limit, order
149
164
  end
165
+
166
+ # Export a set of records
167
+ #
168
+ # @param [Array] ids list of ids of records to export
169
+ # @param [Array] fields list of fields of the selected records to export
170
+ # @param [Hash] context
171
+ def export_data ids, fields, context = nil
172
+ call! :export_data, ids, fields, context
173
+ end
174
+
175
+ # Import a set of records with a single request
176
+ #
177
+ # @param [Array] fields fields to import
178
+ # @param [Array<Hash>] data an Array of Hashes which represent the records to import
179
+ # @param [Hash] options for options see OpenERPs Technical Memento
180
+ def import_data fields, data, options = {}
181
+ options = {
182
+ :mode => 'init',
183
+ :current_module => '',
184
+ :noupdate => false
185
+ }.merge options.symbolize_keys
186
+
187
+ call! :import_data, fields, data, *options.values_at(:mode, :current_module, :noupdate, :context, :filename)
188
+ end
150
189
  end
151
190
  end
152
191
  end
@@ -1,5 +1,18 @@
1
1
  module Kangaroo
2
2
  module Util
3
+
4
+ # Proxy to the Db service (at /xmlrpc/db), specifically
5
+ # to those functions, that need the superadmin password.
6
+ # You can access this Proxy via your client instance:
7
+ # @example
8
+ #
9
+ # config = Kangaroo::Util::Configuration.new 'spec/test_env/test.yml'
10
+ # client = config.client
11
+ # superadmin_proxy = client.superadmin 'admin'
12
+ # superadmin_proxy.drop 'test_database'
13
+ #
14
+ # For functions that need superadmin authentication (like create, drop etc)
15
+ # @see Kangaroo::Util::Proxy::Superadmin
3
16
  class Proxy::Superadmin < Proxy
4
17
  # Create a new database
5
18
  #
@@ -1,3 +1,3 @@
1
1
  module Kangaroo
2
- VERSION = "0.0.3"
2
+ VERSION = "0.1.0.alpha1"
3
3
  end
@@ -0,0 +1 @@
1
+ require 'kangaroo/viz/base'
@@ -0,0 +1,92 @@
1
+ require 'graphviz'
2
+ require 'ruby-debug'
3
+
4
+ module Kangaroo
5
+ module Viz
6
+ class Base
7
+ attr_accessor :namespace, :models, :file, :type
8
+
9
+ def initialize namespace
10
+ @namespace = namespace
11
+ @models = models_in namespace
12
+ end
13
+
14
+ def generate
15
+ @graph = GraphViz.new :G
16
+
17
+ @models.each do |model|
18
+ @graph.add_node model.name, node_opts_for(model)
19
+ end
20
+
21
+ @namespace.dont_load_models = true
22
+ @models.each do |model|
23
+ model.fields.each do |field|
24
+ next unless field.association?
25
+
26
+ @graph.add_edge model.name, field.relation_class.name, edge_opts_for(field) if field.relation_class
27
+ end
28
+ end
29
+
30
+ @graph.output type.to_sym => file
31
+ end
32
+
33
+ def models_in namespace
34
+ models = []
35
+
36
+ namespace.constants.each do |const_name|
37
+ const = namespace.const_get const_name
38
+ models << const if is_a_model?(const)
39
+ models += models_in(const) if is_a_namespace?(const) || is_a_model?(const)
40
+ end
41
+
42
+ models
43
+ end
44
+
45
+ protected
46
+ def node_opts_for model
47
+ {
48
+ :label => "#{model.name}\n#{model.oo_name}"
49
+ }
50
+ end
51
+
52
+ def edge_opts_for field
53
+ color = color_for_association(field)
54
+
55
+ {
56
+ :label => label_for_association(field),
57
+ :color => color,
58
+ :fontcolor => color,
59
+ :fontsize => 10
60
+ }
61
+ end
62
+
63
+ def label_for_association field
64
+ type = case field.type
65
+ when 'one2many' then '1..N'
66
+ when 'many2one' then 'N..1'
67
+ when 'one2one' then '1..1'
68
+ when 'many2many' then 'N..N'
69
+ end
70
+
71
+ "#{type}\n#{field.name}"
72
+ end
73
+
74
+ def color_for_association field
75
+ case field.type
76
+ when 'one2many' then 'green'
77
+ when 'many2one' then 'blue'
78
+ when 'one2one' then 'red'
79
+ when 'many2many' then 'orange'
80
+ end
81
+ end
82
+
83
+ def is_a_model? const
84
+ const.is_a?(Class) && const.superclass == Kangaroo::Model::Base
85
+ end
86
+
87
+ def is_a_namespace? const
88
+ const.is_a?(Module) && const.respond_to?(:namespace)
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+ require 'kangaroo/commands/base'
3
+
4
+ describe Kangaroo::Commands::Base do
5
+
6
+ describe 'configuration, called with' do
7
+ let(:argv) { [] }
8
+ subject { described_class.new(*argv).configuration }
9
+
10
+ context 'with --config file' do
11
+ let(:argv) { %w(--config spec/test_env/test.yml) }
12
+
13
+ its(['host']) { should == "127.0.0.1" }
14
+ its(['database']) { should include('user' => 'admin')}
15
+ its(['database']) { should include('password' => 'admin')}
16
+ its(['database']) { should include('name' => 'kangaroo_test_database')}
17
+
18
+ context 'and --host localhost' do
19
+ let(:argv) { %w(--config spec/test_env/test.yml --host localhost) }
20
+ its(['host']) { should == 'localhost'}
21
+ end
22
+ end
23
+
24
+ context 'short arguments' do
25
+ let(:argv) { %w(-u admin -p admin -d kangaroo_test_database) }
26
+ its(['port']) { should == 8069 }
27
+ its(['host']) { should == "localhost" }
28
+ its(['database']) { should include('user' => 'admin')}
29
+ its(['database']) { should include('password' => 'admin')}
30
+ its(['database']) { should include('name' => 'kangaroo_test_database')}
31
+ end
32
+
33
+ context 'with --user, --password and --database' do
34
+ let(:argv) { %w(--user admin --password admin --database kangaroo_test_database) }
35
+ its(['port']) { should == 8069 }
36
+ its(['host']) { should == "localhost" }
37
+ its(['database']) { should include('user' => 'admin')}
38
+ its(['database']) { should include('password' => 'admin')}
39
+ its(['database']) { should include('name' => 'kangaroo_test_database')}
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,72 @@
1
+ require 'spec_helper'
2
+
3
+ module Kangaroo
4
+ describe Model::Associations, 'Many2one' do
5
+ before :all do
6
+ config = Kangaroo::Util::Configuration.new 'spec/test_env/test.yml'
7
+ config.login
8
+
9
+ Kangaroo::Util::Loader.new(%w(res.partner res.partner.address), config.database, 'AssociationsSpec').load!
10
+ end
11
+
12
+ after :each do
13
+ @cleanup && @cleanup.call
14
+ end
15
+
16
+ it 'reads id and name of associated object' do
17
+ address = AssociationsSpec::Res::Partner::Address.find 1
18
+ partner = AssociationsSpec::Res::Partner.find 1
19
+
20
+ address.partner_id_id.should == 1
21
+ address.partner_id_name.should == partner.name
22
+ end
23
+
24
+ it 'reads id and name of association object after a change' do
25
+ address = AssociationsSpec::Res::Partner::Address.find 1
26
+ address.partner_id_id = 2
27
+ partner = AssociationsSpec::Res::Partner.find 2
28
+
29
+ address.partner_id_id.should == 2
30
+ address.partner_id_name.should == partner.name
31
+ end
32
+
33
+ it 'writes id of object to associate' do
34
+ @cleanup = lambda do
35
+ address = AssociationsSpec::Res::Partner::Address.find 1
36
+ address.partner_id = 1
37
+ address.save!
38
+ end
39
+
40
+ address = AssociationsSpec::Res::Partner::Address.find 1
41
+ address.partner_id_id = 2
42
+ address.save!
43
+
44
+ address = AssociationsSpec::Res::Partner::Address.find 1
45
+ address.partner_id_id.should == 2
46
+ end
47
+
48
+ it 'reads associated object' do
49
+ address = AssociationsSpec::Res::Partner::Address.find 1
50
+ partner = AssociationsSpec::Res::Partner.find 1
51
+
52
+ address.partner_id_id.should == 1
53
+ address.partner_id_obj.should == partner
54
+ end
55
+
56
+ it 'writes association' do
57
+ @cleanup = lambda do
58
+ address = AssociationsSpec::Res::Partner::Address.find 1
59
+ address.partner_id = 1
60
+ address.save!
61
+ end
62
+
63
+ address = AssociationsSpec::Res::Partner::Address.find 1
64
+ partner = AssociationsSpec::Res::Partner.find 2
65
+ address.partner_id_obj = partner
66
+ address.save!
67
+
68
+ address = AssociationsSpec::Res::Partner::Address.find 1
69
+ address.partner_id_id.should == 2
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,65 @@
1
+ require 'spec_helper'
2
+
3
+ module Kangaroo
4
+ describe Model::Associations, 'One2many' do
5
+ before :all do
6
+ config = Kangaroo::Util::Configuration.new 'spec/test_env/test.yml'
7
+ config.login
8
+
9
+ Kangaroo::Util::Loader.new(%w(res.partner res.partner.address), config.database, 'AssociationsSpec').load!
10
+ end
11
+
12
+ after :each do
13
+ @cleanup && @cleanup.call
14
+ end
15
+
16
+ it 'reads ids of associated objects' do
17
+ partner = AssociationsSpec::Res::Partner.find 1
18
+
19
+ partner.address_ids.should == [1]
20
+ end
21
+
22
+ it 'reads ids of associate objects after change' do
23
+ partner = AssociationsSpec::Res::Partner.find 1
24
+
25
+ partner.address_ids = [2]
26
+ partner.address_ids.should == [2]
27
+ end
28
+
29
+ it 'writes ids of objects to associate' do
30
+ @cleanup = lambda do
31
+ partner = AssociationsSpec::Res::Partner.find 1
32
+ partner.address = [[6, 0, [1]]]
33
+ partner.save!
34
+ end
35
+
36
+ partner = AssociationsSpec::Res::Partner.find 1
37
+ partner.address_ids = [2]
38
+ partner.save!
39
+
40
+ partner = AssociationsSpec::Res::Partner.find 1
41
+ partner.address.should == [2]
42
+ end
43
+
44
+ it 'reads associated objects' do
45
+ partner = AssociationsSpec::Res::Partner.find 1
46
+ address = AssociationsSpec::Res::Partner::Address.find 1
47
+
48
+ partner.address_objs.should be_an Array
49
+ partner.address_objs.to_a.should == [address]
50
+ end
51
+
52
+ it 'writes association' do
53
+ @cleanup = lambda do
54
+ address = AssociationsSpec::Res::Partner::Address.find 1
55
+ address.partner_id = 1
56
+ address.save!
57
+ end
58
+
59
+ address = AssociationsSpec::Res::Partner::Address.find 1
60
+ partner = AssociationsSpec::Res::Partner.find 2
61
+
62
+ partner.address_objs = [address]
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ module Kangaroo
4
+ describe 'Common service' do
5
+ before :all do
6
+ @config = Kangaroo::Util::Configuration.new 'spec/test_env/test.yml'
7
+ @config.login
8
+
9
+ Kangaroo::Util::Loader.new('res.partner', @config.database, 'CommonServiceSpec').load!
10
+ end
11
+
12
+ it "can change log levels" do
13
+ @config.client.common.set_loglevel('admin', :debug).should == true
14
+ @config.client.common.set_loglevel 'admin', :error
15
+ end
16
+
17
+ it "can read number of sql queries executed" do
18
+ @config.client.common.set_loglevel 'admin', :debug_sql
19
+
20
+ CommonServiceSpec::Res::Partner.first
21
+ @config.client.common.get_sqlcount('admin').should > 0
22
+ @config.client.common.set_loglevel 'admin', :error
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+
3
+ module Kangaroo
4
+ describe Model::DataImport do
5
+ before :all do
6
+ config = Kangaroo::Util::Configuration.new 'spec/test_env/test.yml'
7
+ config.login
8
+
9
+ Kangaroo::Util::Loader.new('res.country', config.database, 'DataImportSpec').load!
10
+ end
11
+
12
+ after :each do
13
+ @cleanup && @cleanup.call
14
+ end
15
+
16
+ it 'updates records' do
17
+ a = DataImportSpec::Res::Country.find 1
18
+
19
+ old_name = a.name
20
+ @cleanup = lambda do
21
+ a = DataImportSpec::Res::Country.find 1
22
+ a.name = old_name
23
+ a.save!
24
+ end
25
+ a.name = 'abcdefg'
26
+
27
+ old_count = DataImportSpec::Res::Country.count
28
+ DataImportSpec::Res::Country.import_all [a]
29
+ DataImportSpec::Res::Country.count.should == old_count
30
+
31
+ DataImportSpec::Res::Country.find(1).name.should == 'abcdefg'
32
+ end
33
+
34
+ it 'creates new records' do
35
+ a = DataImportSpec::Res::Country.new :code => 'XY', :name => 'a_new_country'
36
+
37
+ @cleanup = lambda do
38
+ DataImportSpec::Res::Country.where(:code => 'XY').first.destroy
39
+ end
40
+
41
+ old_count = DataImportSpec::Res::Country.count
42
+ puts DataImportSpec::Res::Country.import_all([a]).inspect
43
+ DataImportSpec::Res::Country.count.should == old_count + 1
44
+
45
+ DataImportSpec::Res::Country.where(:code => 'XY').first.name.should == 'a_new_country'
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+
3
+ module Kangaroo
4
+ describe Model::DynamicFinder do
5
+ before :all do
6
+ config = Kangaroo::Util::Configuration.new 'spec/test_env/test.yml'
7
+ config.login
8
+
9
+ Kangaroo::Util::Loader.new('res.country', config.database, 'DynamicFinderSpec').load!
10
+ end
11
+
12
+ after :each do
13
+ @cleanup && @cleanup.call
14
+ end
15
+
16
+ it 'catches find_by_* calls' do
17
+ DynamicFinderSpec::Res::Country.should respond_to :find_by_code
18
+ DynamicFinderSpec::Res::Country.find_by_code('DE').code.should == 'DE'
19
+ end
20
+
21
+ it 'captches find_by_*_and_* calls' do
22
+ DynamicFinderSpec::Res::Country.should respond_to :find_by_code_and_name
23
+ DynamicFinderSpec::Res::Country.find_by_code_and_name('DE', 'Germany').name.should == 'Germany'
24
+ end
25
+
26
+ it 'Dynamic Finder can be used on relations' do
27
+ DynamicFinderSpec::Res::Country.select(:code).find_by_code('DE').code.should == 'DE'
28
+ end
29
+
30
+ it 'catches find_all_by_* calls' do
31
+ DynamicFinderSpec::Res::Country.find_all_by_code('DE').should be_an Array
32
+ DynamicFinderSpec::Res::Country.find_all_by_code('DE').first.code.should == 'DE'
33
+ end
34
+ end
35
+ end