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.
- data/.gitignore +1 -0
- data/.rspec +2 -0
- data/.yardopts +6 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +66 -0
- data/LICENSE +22 -0
- data/README.md +65 -0
- data/Rakefile +36 -0
- data/config/kangaroo.yml.sample +12 -0
- data/docs/AdditionalServices.md +3 -0
- data/docs/Architecture.md +50 -0
- data/docs/Installation.md +73 -0
- data/docs/Usage.md +161 -0
- data/features/connection.feature +5 -0
- data/features/env.rb +8 -0
- data/features/step_definitions/basic_steps.rb +24 -0
- data/features/step_definitions/connection_steps.rb +13 -0
- data/features/support/test.yml +11 -0
- data/features/utility_services.feature +39 -0
- data/kangaroo.gemspec +29 -0
- data/lib/kangaroo.rb +6 -0
- data/lib/kangaroo/exception.rb +7 -0
- data/lib/kangaroo/model/attributes.rb +124 -0
- data/lib/kangaroo/model/base.rb +70 -0
- data/lib/kangaroo/model/condition_normalizer.rb +63 -0
- data/lib/kangaroo/model/default_attributes.rb +22 -0
- data/lib/kangaroo/model/field.rb +20 -0
- data/lib/kangaroo/model/finder.rb +92 -0
- data/lib/kangaroo/model/inspector.rb +55 -0
- data/lib/kangaroo/model/open_object_orm.rb +117 -0
- data/lib/kangaroo/model/persistence.rb +180 -0
- data/lib/kangaroo/model/relation.rb +212 -0
- data/lib/kangaroo/model/remote_execute.rb +29 -0
- data/lib/kangaroo/railtie.rb +13 -0
- data/lib/kangaroo/ruby_adapter/base.rb +28 -0
- data/lib/kangaroo/ruby_adapter/class_definition.rb +46 -0
- data/lib/kangaroo/ruby_adapter/fields.rb +18 -0
- data/lib/kangaroo/util/client.rb +59 -0
- data/lib/kangaroo/util/configuration.rb +82 -0
- data/lib/kangaroo/util/database.rb +92 -0
- data/lib/kangaroo/util/loader.rb +98 -0
- data/lib/kangaroo/util/loader/model.rb +21 -0
- data/lib/kangaroo/util/loader/namespace.rb +15 -0
- data/lib/kangaroo/util/proxy.rb +35 -0
- data/lib/kangaroo/util/proxy/common.rb +111 -0
- data/lib/kangaroo/util/proxy/db.rb +34 -0
- data/lib/kangaroo/util/proxy/object.rb +153 -0
- data/lib/kangaroo/util/proxy/report.rb +24 -0
- data/lib/kangaroo/util/proxy/superadmin.rb +71 -0
- data/lib/kangaroo/util/proxy/wizard.rb +25 -0
- data/lib/kangaroo/util/proxy/workflow.rb +14 -0
- data/lib/kangaroo/version.rb +3 -0
- data/spec/model/attributes_spec.rb +70 -0
- data/spec/model/base_spec.rb +19 -0
- data/spec/model/default_attributes_spec.rb +37 -0
- data/spec/model/finder_spec.rb +104 -0
- data/spec/model/inspector_spec.rb +56 -0
- data/spec/model/open_object_orm_spec.rb +134 -0
- data/spec/model/persistence_spec.rb +53 -0
- data/spec/model/relation_spec.rb +122 -0
- data/spec/ruby_adapter/class_definition_spec.rb +51 -0
- data/spec/server_helper.rb +167 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/test_env/test.yml +11 -0
- data/spec/util/configuration_spec.rb +36 -0
- data/spec/util/loader_spec.rb +50 -0
- data/spec/util/proxy_spec.rb +61 -0
- 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
|
data/spec/spec_helper.rb
ADDED
@@ -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
|