kangaroo 0.0.1.pre

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.
Files changed (68) hide show
  1. data/.gitignore +1 -0
  2. data/.rspec +2 -0
  3. data/.yardopts +6 -0
  4. data/Gemfile +2 -0
  5. data/Gemfile.lock +66 -0
  6. data/LICENSE +22 -0
  7. data/README.md +65 -0
  8. data/Rakefile +36 -0
  9. data/config/kangaroo.yml.sample +12 -0
  10. data/docs/AdditionalServices.md +3 -0
  11. data/docs/Architecture.md +50 -0
  12. data/docs/Installation.md +73 -0
  13. data/docs/Usage.md +161 -0
  14. data/features/connection.feature +5 -0
  15. data/features/env.rb +8 -0
  16. data/features/step_definitions/basic_steps.rb +24 -0
  17. data/features/step_definitions/connection_steps.rb +13 -0
  18. data/features/support/test.yml +11 -0
  19. data/features/utility_services.feature +39 -0
  20. data/kangaroo.gemspec +29 -0
  21. data/lib/kangaroo.rb +6 -0
  22. data/lib/kangaroo/exception.rb +7 -0
  23. data/lib/kangaroo/model/attributes.rb +124 -0
  24. data/lib/kangaroo/model/base.rb +70 -0
  25. data/lib/kangaroo/model/condition_normalizer.rb +63 -0
  26. data/lib/kangaroo/model/default_attributes.rb +22 -0
  27. data/lib/kangaroo/model/field.rb +20 -0
  28. data/lib/kangaroo/model/finder.rb +92 -0
  29. data/lib/kangaroo/model/inspector.rb +55 -0
  30. data/lib/kangaroo/model/open_object_orm.rb +117 -0
  31. data/lib/kangaroo/model/persistence.rb +180 -0
  32. data/lib/kangaroo/model/relation.rb +212 -0
  33. data/lib/kangaroo/model/remote_execute.rb +29 -0
  34. data/lib/kangaroo/railtie.rb +13 -0
  35. data/lib/kangaroo/ruby_adapter/base.rb +28 -0
  36. data/lib/kangaroo/ruby_adapter/class_definition.rb +46 -0
  37. data/lib/kangaroo/ruby_adapter/fields.rb +18 -0
  38. data/lib/kangaroo/util/client.rb +59 -0
  39. data/lib/kangaroo/util/configuration.rb +82 -0
  40. data/lib/kangaroo/util/database.rb +92 -0
  41. data/lib/kangaroo/util/loader.rb +98 -0
  42. data/lib/kangaroo/util/loader/model.rb +21 -0
  43. data/lib/kangaroo/util/loader/namespace.rb +15 -0
  44. data/lib/kangaroo/util/proxy.rb +35 -0
  45. data/lib/kangaroo/util/proxy/common.rb +111 -0
  46. data/lib/kangaroo/util/proxy/db.rb +34 -0
  47. data/lib/kangaroo/util/proxy/object.rb +153 -0
  48. data/lib/kangaroo/util/proxy/report.rb +24 -0
  49. data/lib/kangaroo/util/proxy/superadmin.rb +71 -0
  50. data/lib/kangaroo/util/proxy/wizard.rb +25 -0
  51. data/lib/kangaroo/util/proxy/workflow.rb +14 -0
  52. data/lib/kangaroo/version.rb +3 -0
  53. data/spec/model/attributes_spec.rb +70 -0
  54. data/spec/model/base_spec.rb +19 -0
  55. data/spec/model/default_attributes_spec.rb +37 -0
  56. data/spec/model/finder_spec.rb +104 -0
  57. data/spec/model/inspector_spec.rb +56 -0
  58. data/spec/model/open_object_orm_spec.rb +134 -0
  59. data/spec/model/persistence_spec.rb +53 -0
  60. data/spec/model/relation_spec.rb +122 -0
  61. data/spec/ruby_adapter/class_definition_spec.rb +51 -0
  62. data/spec/server_helper.rb +167 -0
  63. data/spec/spec_helper.rb +14 -0
  64. data/spec/test_env/test.yml +11 -0
  65. data/spec/util/configuration_spec.rb +36 -0
  66. data/spec/util/loader_spec.rb +50 -0
  67. data/spec/util/proxy_spec.rb +61 -0
  68. metadata +260 -0
@@ -0,0 +1,53 @@
1
+ require 'spec_helper'
2
+ require 'kangaroo/model/base'
3
+
4
+ module Kangaroo
5
+ module Model
6
+ describe Persistence do
7
+
8
+ before :each do
9
+ @klass = Class.new(Kangaroo::Model::Base)
10
+ @klass.define_multiple_accessors :a, :b
11
+ @klass.stub!(:default_attributes).and_return({})
12
+ end
13
+
14
+ context '#instantiate' do
15
+ it 'raises error if id is missing' do
16
+ lambda { @klass.send(:instantiate, {}) }.should raise_error(Kangaroo::InstantiatedRecordNeedsIDError)
17
+ end
18
+
19
+ it 'returns an instance' do
20
+ object = @klass.send :instantiate, {:id => 1, :a => 'one'}
21
+ object.should be_an_instance_of(@klass)
22
+ end
23
+
24
+ it 'sets attributes' do
25
+ object = @klass.send :instantiate, {:id => 1, :a => 'one'}
26
+ object.a.should == 'one'
27
+ end
28
+ end
29
+
30
+
31
+ describe '#find' do
32
+ it 'raises RecordNotFound error if no record was found' do
33
+ @remote_stub = mock 'remote'
34
+ @klass.stub!(:remote).and_return @remote_stub
35
+
36
+ @remote_stub.stub!(:read).
37
+ and_return []
38
+ lambda {@klass.find(1)}.should raise_error(Kangaroo::RecordNotFound)
39
+ end
40
+ end
41
+
42
+ it 'marks initialized record as new' do
43
+ object = @klass.new
44
+ object.should be_a_new_record
45
+ end
46
+
47
+ it 'marks instantiated record as persisted' do
48
+ object = @klass.send :instantiate, {:id => 1}
49
+ object.should be_persisted
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,122 @@
1
+ require 'spec_helper'
2
+
3
+ module Kangaroo
4
+ module Model
5
+ describe Relation do
6
+ before do
7
+ @target = mock 'target'
8
+ @relation = Relation.new @target
9
+ end
10
+
11
+ it 'delegates array methods via target#all' do
12
+ array_mock = mock 'array'
13
+ array_mock.should_receive :each
14
+
15
+ @target.should_receive(:search_and_read).and_return array_mock
16
+ @relation.send :each
17
+ end
18
+
19
+ it 'stores conditions via where() and hands them over to target#all' do
20
+ @target.should_receive(:search_and_read).with [{:a => :b}], anything, anything
21
+ @relation.where(:a => :b).all
22
+ end
23
+
24
+ it 'merges chained where() conditions' do
25
+ @target.should_receive(:search_and_read).with [{:a => :b}, {:b => :c}], anything, anything
26
+ @relation.where(:a => :b).where(:b => :c).all
27
+ end
28
+
29
+ it 'doesnt change the relation on chaining' do
30
+ @target.should_receive(:search_and_read).with [{:a => :b}], anything, anything
31
+ chained_relation = @relation.where(:a => :b)
32
+ chained_relation.where(:b => :c)
33
+ chained_relation.all
34
+ end
35
+
36
+ it 'stores limit and hands it over to target#all' do
37
+ @target.should_receive(:search_and_read).with [], hash_including(:limit => 10), anything
38
+ @relation.limit(10).all
39
+ end
40
+
41
+ it 'overwrites limit clauses when chained' do
42
+ @target.should_receive(:search_and_read).with [], hash_including(:limit => 10), anything
43
+ @relation.limit(20).limit(10).all
44
+ end
45
+
46
+ it 'stores offset and hands it over to target#all' do
47
+ @target.should_receive(:search_and_read).with [], hash_including(:offset => 10), anything
48
+ @relation.offset(10).all
49
+ end
50
+
51
+ it 'overwrites offset clauses when chained' do
52
+ @target.should_receive(:search_and_read).with [], hash_including(:offset => 10), anything
53
+ @relation.offset(20).offset(10).all
54
+ end
55
+
56
+ it 'stores select and hands it over to target#all' do
57
+ @target.should_receive(:search_and_read).
58
+ with [], anything, hash_including(:fields => ["a", "b"])
59
+ @relation.select(:a, :b).all
60
+ end
61
+
62
+ it 'stores context hands it over to target#all' do
63
+ @target.should_receive(:search_and_read).
64
+ with [], hash_including(:context => {:lang => 'de_DE'}), hash_including(:context => {:lang => 'de_DE'})
65
+ @relation.context(:lang => 'de_DE').all
66
+ end
67
+
68
+ it 'merges chained contexts' do
69
+ @target.should_receive(:search_and_read).
70
+ with [], hash_including(:context => {:lang => 'de_DE', :tz => 'de'}), hash_including(:context => {:lang => 'de_DE', :tz => 'de'})
71
+ @relation.context(:lang => 'de_DE').context(:tz => 'de').all
72
+ end
73
+
74
+ describe '#reverse' do
75
+ it 'adds descending id order if no order was specified' do
76
+ @relation.reverse.order_clause.should == ['id desc']
77
+ end
78
+
79
+ it 'reverses previously specified orders' do
80
+ @relation.order('name').reverse.order_clause.should == ['name desc']
81
+ @relation.order('name', true).reverse.order_clause.should == ['name']
82
+ @relation.order('name').order('code', true).reverse.order_clause.should == ['name desc', 'code']
83
+ end
84
+ end
85
+
86
+ describe '#[]' do
87
+ it 'doesnt touch existing limit clause' do
88
+ @target.should_receive(:search_and_read).
89
+ with([], hash_including(:limit => 2), anything).
90
+ and_return []
91
+ @relation.limit(2)[1]
92
+ end
93
+
94
+ it 'doesnt touch existing offset clause' do
95
+ @target.should_receive(:search_and_read).
96
+ with([], hash_including(:offset => 40), anything).
97
+ and_return []
98
+ @relation.offset(40)[10]
99
+ end
100
+
101
+ it '[n] returns the n-th record' do
102
+ @target.should_receive(:search_and_read).
103
+ with([], hash_including(:limit => 1, :offset => 21), anything).
104
+ and_return []
105
+ @relation[21]
106
+ end
107
+
108
+ it '[n, m] return m records skipping n' do
109
+ @target.should_receive(:search_and_read).
110
+ with [], hash_including(:limit => 4, :offset => 21), anything
111
+ @relation[21, 4]
112
+ end
113
+
114
+ it '[n..m] returns records n to m' do
115
+ @target.should_receive(:search_and_read).
116
+ with [], hash_including(:limit => 5, :offset => 21), anything
117
+ @relation[21..25]
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,51 @@
1
+ require 'spec_helper'
2
+ require 'server_helper'
3
+
4
+ require 'kangaroo/ruby_adapter/base'
5
+
6
+ module Kangaroo
7
+ module RubyAdapter
8
+ describe ClassDefinition do
9
+ def stub_oo_model name
10
+ oo_model = mock 'oo_model'
11
+ oo_model.stub!('model_class_name').and_return(name)
12
+ oo_model
13
+ end
14
+
15
+ def adapt_oo_model name
16
+ oo_model = stub_oo_model name
17
+ adapter = Base.new(oo_model)
18
+ adapter.send :define_class
19
+ end
20
+
21
+ it 'creates a class in Oo for a (theoretical) top-level OpenObject model' do
22
+ ruby_model = adapt_oo_model 'Oo::User'
23
+
24
+ ruby_model.name.should == 'Oo::User'
25
+ ruby_model.superclass.should be(Kangaroo::Model::Base)
26
+ end
27
+
28
+ it 'creates module namespaces for nested OpenObject models' do
29
+ ruby_model = adapt_oo_model 'Oo::A::B::User'
30
+
31
+ ruby_model.name.should == 'Oo::A::B::User'
32
+ Oo::A.should be_a(Module)
33
+ Oo::A::B.should be_a(Module)
34
+ end
35
+
36
+ it 'nests models in classes if a parent model is already loaded' do
37
+ parent_model = adapt_oo_model 'Oo::C::D'
38
+ ruby_model = adapt_oo_model 'Oo::C::D::E'
39
+
40
+ Oo::C::D.should be_a(Class)
41
+ Oo::C::D::E.should be_a(Class)
42
+ end
43
+
44
+ it 'raises an error if trying to define a parent model after a child model' do
45
+ child_model = adapt_oo_model 'Oo::Sale::Order::Line'
46
+
47
+ lambda { adapt_oo_model('Oo::Sale::Order') }.should raise_error(Kangaroo::ChildDefinedBeforeParentError)
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,167 @@
1
+ require 'rubygems'
2
+ require 'rapuncel'
3
+ require 'active_support/core_ext/string'
4
+ require "xmlrpc/server"
5
+ require 'webrick'
6
+
7
+ module TestServices
8
+ class ObjectService
9
+ def execute *args
10
+ xmlrpc_call 'execute', *args
11
+ end
12
+
13
+ protected
14
+ def xmlrpc_call name, *args
15
+ end
16
+ end
17
+
18
+ class CommonService
19
+ def login *args
20
+ xmlrpc_call 'login', *args
21
+ args
22
+ end
23
+
24
+ def some_method *args
25
+ xmlrpc_call 'some_method', *args
26
+ args
27
+ end
28
+
29
+ protected
30
+ def xmlrpc_call name, *args
31
+ end
32
+ end
33
+
34
+ class DbService
35
+ def list *args
36
+ xmlrpc_call 'list', *args
37
+ args
38
+ end
39
+
40
+ def rename *args
41
+ xmlrpc_call 'rename', *args
42
+ args
43
+ end
44
+
45
+ protected
46
+ def xmlrpc_call name, *args
47
+ end
48
+ end
49
+
50
+ class ReportService
51
+ def create *args
52
+ xmlrpc_call 'create', *args
53
+ args
54
+ end
55
+
56
+ protected
57
+ def xmlrpc_call name, *args
58
+ end
59
+ end
60
+
61
+ class WizardService
62
+ def create *args
63
+ xmlrpc_call 'create', *args
64
+ end
65
+
66
+ protected
67
+ def xmlrpc_call name, *args
68
+ end
69
+ end
70
+ end
71
+
72
+ module TestServerHelper
73
+ def self.included klass
74
+ klass.before :all do
75
+ @test_server = TestServer.start
76
+ end
77
+
78
+ klass.after :all do
79
+ @test_server.stop
80
+ end
81
+ end
82
+
83
+ def client service
84
+ Rapuncel::Client.new :host => '127.0.0.1', :port => 8069, :path => "/xmlrpc/#{service}"
85
+ end
86
+
87
+ def proxy service, *args
88
+ Kangaroo::Util::Proxy.const_get(service.camelize).new client(service), *args
89
+ end
90
+
91
+ def object_service
92
+ @test_server.object_service
93
+ end
94
+
95
+ def common_service
96
+ @test_server.common_service
97
+ end
98
+
99
+ def db_service
100
+ @test_server.db_service
101
+ end
102
+
103
+ def report_service
104
+ @test_server.report_service
105
+ end
106
+
107
+ def wizard_service
108
+ @test_server.wizard_service
109
+ end
110
+ end
111
+
112
+ class TestServer
113
+ SERVICES = %w(ObjectService CommonService DbService WizardService ReportService)
114
+ SERVICES.each do |service|
115
+ attr_accessor service.underscore
116
+ end
117
+ attr_accessor :server
118
+
119
+ def initialize
120
+ @server = WEBrick::HTTPServer.new(:Port => 8069, :BindAddress => '127.0.0.1', :MaxClients => 1,
121
+ :Logger => WEBrick::Log.new(File.open('/dev/null', 'w')))
122
+
123
+ SERVICES.each do |service|
124
+ mount_service service
125
+ end
126
+ end
127
+
128
+ def serve
129
+ if RUBY_PLATFORM =~ /mingw|mswin32/
130
+ signals = [1]
131
+ else
132
+ signals = %w[INT TERM HUP]
133
+ end
134
+ signals.each { |signal| trap(signal) { @server.shutdown } }
135
+
136
+ @server.start
137
+ end
138
+
139
+ def mount_service service
140
+ server = XMLRPC::WEBrickServlet.new ''
141
+ add_handler server, service
142
+ @server.mount "/xmlrpc/#{mount_point(service)}", server
143
+ end
144
+
145
+ def mount_point service
146
+ service.underscore.sub '_service', ''
147
+ end
148
+
149
+ def add_handler server, service
150
+ const = "TestServices::#{service}".constantize
151
+ inst = const.new
152
+ send "#{service.underscore}=", inst
153
+ server.add_handler(XMLRPC::iPIMethods(''), inst)
154
+ end
155
+
156
+ def stop
157
+ @server.shutdown
158
+ end
159
+
160
+ def self.start
161
+ new.tap do |s|
162
+ Thread.new do
163
+ s.serve
164
+ end
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,14 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ require 'kangaroo'
5
+
6
+ module SpecHelper
7
+ def anythings count
8
+ [anything] * count
9
+ end
10
+ end
11
+
12
+ RSpec.configure do |config|
13
+ # some (optional) config here
14
+ end
@@ -0,0 +1,11 @@
1
+ host: 127.0.0.1
2
+ port: 8069
3
+
4
+ database:
5
+ name: kangaroo_test_database
6
+ user: admin
7
+ password: admin
8
+
9
+ models:
10
+ - res.*
11
+ - product.product
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+ require 'server_helper'
3
+
4
+ require 'kangaroo/util/configuration'
5
+
6
+ module Kangaroo
7
+ module Util
8
+ describe Configuration do
9
+ include TestServerHelper
10
+
11
+ def config_file
12
+ File.join File.dirname(__FILE__), '..', 'test_env', 'test.yml'
13
+ end
14
+
15
+ it "configures Kangaroo by config file" do
16
+ config = Kangaroo::Util::Configuration.new(config_file, Logger.new('/dev/null'))
17
+
18
+ config.models.should == ['res.*']
19
+ config.database.db_name.should == 'kangaroo_test_database'
20
+ config.database.user.should == 'admin'
21
+ config.database.password.should == 'admin'
22
+ end
23
+
24
+ it 'authorizes the configured user before model loading' do
25
+ config = Kangaroo::Util::Configuration.new(config_file, Logger.new('/dev/null'))
26
+ loader = mock('loader')
27
+ loader.stub! :load!
28
+ Loader.stub!(:new).and_return loader
29
+
30
+ common_service.should_receive(:xmlrpc_call).
31
+ with('login', 'kangaroo_test_database', 'admin', 'admin')
32
+ config.load_models
33
+ end
34
+ end
35
+ end
36
+ end