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,29 @@
|
|
1
|
+
module Kangaroo
|
2
|
+
module Model
|
3
|
+
module RemoteExecute
|
4
|
+
def call name, *args
|
5
|
+
return_value = remote.send name, ids_for_execute, *args
|
6
|
+
|
7
|
+
# TODO: handle warnings etc
|
8
|
+
if Hash === return_value && return_value[:value]
|
9
|
+
handle_updated_values return_value[:value]
|
10
|
+
self
|
11
|
+
else
|
12
|
+
return_value
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
protected
|
17
|
+
def handle_updated_values values
|
18
|
+
values.each do |key, value|
|
19
|
+
write_attribute key, value
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
def ids_for_execute
|
25
|
+
new_record? ? [] : [id]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Kangaroo
|
2
|
+
class Railtie < Rails::Railtie
|
3
|
+
initializer 'kangaroo.initialize' do
|
4
|
+
begin
|
5
|
+
config_file = File.join(Rails.root, %w(config kangaroo.yml))
|
6
|
+
configuration = Kangaroo::Util::Configuration.new config_file, Rails.logger
|
7
|
+
configuration.load_models
|
8
|
+
rescue Errno::ECONNREFUSED => e
|
9
|
+
Rails.logger.error "Could not connect to OpenERP XML-RPC Service."
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'kangaroo/ruby_adapter/class_definition'
|
2
|
+
require 'kangaroo/ruby_adapter/fields'
|
3
|
+
|
4
|
+
module Kangaroo
|
5
|
+
module RubyAdapter
|
6
|
+
class Base
|
7
|
+
include ClassDefinition
|
8
|
+
include Fields
|
9
|
+
|
10
|
+
attr_accessor :oo_model, :root_namespace
|
11
|
+
|
12
|
+
def initialize model
|
13
|
+
@oo_model = model
|
14
|
+
@root_namespace = model.class.namespace
|
15
|
+
end
|
16
|
+
|
17
|
+
# Adapt the OpenERP model to ruby
|
18
|
+
#
|
19
|
+
# return [Class] A Kangaroo::Model::Base subclass representing the OpenERP model
|
20
|
+
def to_ruby
|
21
|
+
define_class
|
22
|
+
add_fields
|
23
|
+
|
24
|
+
@ruby_model
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'kangaroo/model/base'
|
2
|
+
|
3
|
+
module Kangaroo
|
4
|
+
module RubyAdapter
|
5
|
+
module ClassDefinition
|
6
|
+
def define_class
|
7
|
+
initialize_namespace
|
8
|
+
define_model_class
|
9
|
+
end
|
10
|
+
|
11
|
+
protected
|
12
|
+
def model_subclass
|
13
|
+
Class.new(Kangaroo::Model::Base)
|
14
|
+
end
|
15
|
+
|
16
|
+
def define_model_class
|
17
|
+
@ruby_model = set_const_in @namespace, constant_names.last, model_subclass
|
18
|
+
|
19
|
+
if !@ruby_model.is_a?(Class)
|
20
|
+
raise ChildDefinedBeforeParentError
|
21
|
+
end
|
22
|
+
@ruby_model.database = @oo_model.class.database
|
23
|
+
|
24
|
+
@ruby_model
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize_namespace
|
28
|
+
@namespace = @root_namespace
|
29
|
+
|
30
|
+
constant_names[1..-2].each do |mod|
|
31
|
+
@namespace = set_const_in @namespace, mod, Module.new
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def constant_names
|
36
|
+
@constant_names ||= @oo_model.model_class_name.split("::")
|
37
|
+
end
|
38
|
+
|
39
|
+
# Set constant only if not already defined
|
40
|
+
def set_const_in mod, name, const
|
41
|
+
mod.const_set name, const unless mod.const_defined?(name)
|
42
|
+
mod.const_get name
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Kangaroo
|
2
|
+
module RubyAdapter
|
3
|
+
module Fields
|
4
|
+
def add_fields
|
5
|
+
@ruby_model.define_multiple_accessors *field_names
|
6
|
+
end
|
7
|
+
|
8
|
+
protected
|
9
|
+
def field_names
|
10
|
+
fields.map &:name
|
11
|
+
end
|
12
|
+
|
13
|
+
def fields
|
14
|
+
@fields ||= @ruby_model.fields
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'rapuncel'
|
2
|
+
require 'kangaroo/util/database'
|
3
|
+
require 'kangaroo/util/proxy'
|
4
|
+
|
5
|
+
module Kangaroo
|
6
|
+
module Util
|
7
|
+
class Client < Rapuncel::Client
|
8
|
+
SERVICES = %w(db common object wizard report).freeze
|
9
|
+
|
10
|
+
# Initialize a Kangaroo XMLRPC Client
|
11
|
+
#
|
12
|
+
# @param [Hash] configuration configuration Hash
|
13
|
+
# @option configuration [String] 'host' Hostname or IP address
|
14
|
+
# @option configuration [String, Number] 'port' Port
|
15
|
+
def initialize configuration
|
16
|
+
super configuration.merge(:raise_on => :both)
|
17
|
+
end
|
18
|
+
|
19
|
+
# @private
|
20
|
+
def clone
|
21
|
+
super.tap do |c|
|
22
|
+
c.connection = connection.clone
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
SERVICES.each do |name|
|
27
|
+
class_eval <<-RUBY
|
28
|
+
def #{name}_service
|
29
|
+
@#{name}_service ||= clone.tap do |c|
|
30
|
+
c.connection.path = '/xmlrpc/#{name}'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
RUBY
|
34
|
+
end
|
35
|
+
|
36
|
+
# Access the {Kangaroo::Util::Proxy::Superadmin Superadmin Proxy}
|
37
|
+
#
|
38
|
+
# @param [String] super_password Superadmin password
|
39
|
+
# @return [Kangaroo::Util::Proxy::Superadmin] Superadmin proxy
|
40
|
+
def superadmin super_password
|
41
|
+
Proxy::Superadmin.new db_service, super_password
|
42
|
+
end
|
43
|
+
|
44
|
+
# Access the {Kangaroo::Util::Proxy::Common Common Proxy}
|
45
|
+
#
|
46
|
+
# @return [Kangaroo::Util::Proxy::Common] Common proxy
|
47
|
+
def common
|
48
|
+
@common_proxy ||= Proxy::Common.new common_service
|
49
|
+
end
|
50
|
+
|
51
|
+
# Access the {Kangaroo::Util::Proxy::Db Db Proxy}
|
52
|
+
#
|
53
|
+
# @return [Kangaroo::Util::Proxy::Db] Db proxy
|
54
|
+
def db
|
55
|
+
@db_proxy ||= Proxy::Db.new db_service
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'active_support/core_ext/hash'
|
3
|
+
require 'kangaroo/util/client'
|
4
|
+
require 'kangaroo/util/loader'
|
5
|
+
|
6
|
+
module Kangaroo
|
7
|
+
module Util
|
8
|
+
class Configuration
|
9
|
+
attr_accessor :logger, :database, :models, :client
|
10
|
+
|
11
|
+
# Initialize the Kangaroo configuration
|
12
|
+
#
|
13
|
+
# @param [Hash, String] config_file_or_hash Configuration filename or Hash
|
14
|
+
# @param [Logger] logger logger instance
|
15
|
+
def initialize config_file_or_hash, logger = Logger.new(STDOUT)
|
16
|
+
@logger = logger
|
17
|
+
case config_file_or_hash
|
18
|
+
when String
|
19
|
+
configure_by_file config_file_or_hash
|
20
|
+
when Hash
|
21
|
+
configure_by_hash config_file_or_hash
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Load configured models with {Kangaroo::Util::Loader Loader}
|
26
|
+
#
|
27
|
+
def load_models
|
28
|
+
if models.blank?
|
29
|
+
logger.info "No models to load."
|
30
|
+
return
|
31
|
+
end
|
32
|
+
|
33
|
+
login
|
34
|
+
Loader.new(@database, models).load!
|
35
|
+
logger.info "Loaded OpenERP models matching #{models.inspect}."
|
36
|
+
rescue Exception => e
|
37
|
+
logger.error "Loading of OpenERP models failed.\n#{e.inspect}"
|
38
|
+
end
|
39
|
+
|
40
|
+
# Configure Kangaroo by YAML file
|
41
|
+
#
|
42
|
+
# @param [String] filename Filename to load YAML configuration from
|
43
|
+
def configure_by_file filename
|
44
|
+
configure_by_hash YAML.load_file(filename)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Configure Kangaroo by Hash
|
48
|
+
#
|
49
|
+
# @param [Hash] config configuration Hash
|
50
|
+
# @option config [String] 'host' Hostname or IP address
|
51
|
+
# @option config [String, Number] 'port' Port
|
52
|
+
# @option config [Hash] 'database' Configuration for database
|
53
|
+
def configure_by_hash config
|
54
|
+
@client = Client.new config.slice('host', 'port')
|
55
|
+
configure_database config['database']
|
56
|
+
end
|
57
|
+
|
58
|
+
# Configure an OpenERP database
|
59
|
+
#
|
60
|
+
# @param [Hash] db_config database configuration hash
|
61
|
+
# @option db_config [String] 'name' Name of database
|
62
|
+
# @option db_config [String] 'user' Username to use for authentication
|
63
|
+
# @option db_config [String] 'password' Password for authentication
|
64
|
+
# @option db_config [Enumerable] 'models' List of models(-patterns) to load
|
65
|
+
def configure_database db_config
|
66
|
+
@database = Database.new @client, *db_config.values_at('name', 'user', 'password')
|
67
|
+
@models = db_config['models']
|
68
|
+
logger.info %Q(Configured OpenERP database "#{db_config['name']}" at "#{client.connection.host}")
|
69
|
+
end
|
70
|
+
|
71
|
+
# Login to the configured database
|
72
|
+
#
|
73
|
+
def login
|
74
|
+
if @database.login
|
75
|
+
logger.info %Q(Authenticated user "#{@database.user}" for OpenERP database "#{@database.db_name}")
|
76
|
+
else
|
77
|
+
logger.warn %Q(Login to OpenERP database "#{@database.db_name}" with user "#{@database.user}" failed!)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
module Kangaroo
|
2
|
+
module Util
|
3
|
+
class Database
|
4
|
+
attr_accessor :db_name, :user, :password, :user_id, :client
|
5
|
+
|
6
|
+
# Initialize a new OpenERP database configuration
|
7
|
+
#
|
8
|
+
# @param client A {Kangaroo::Util::Client Client} instance
|
9
|
+
# @param name Database name to configure
|
10
|
+
# @param user Username
|
11
|
+
# @param password Password
|
12
|
+
def initialize client, name, user, password
|
13
|
+
@client, @db_name, @user, @password = client, name, user, password
|
14
|
+
end
|
15
|
+
|
16
|
+
# Access to the {Kangaroo::Util::Proxy::Superadmin Superadmin Proxy}
|
17
|
+
#
|
18
|
+
# @param super_password superadmin password
|
19
|
+
# @return superadmin service proxy
|
20
|
+
def superadmin super_password
|
21
|
+
client.superadmin super_password
|
22
|
+
end
|
23
|
+
|
24
|
+
# Access to the {Kangaroo::Util::Proxy::Db Database Proxy}
|
25
|
+
#
|
26
|
+
# @return database service proxy
|
27
|
+
def db
|
28
|
+
client.db
|
29
|
+
end
|
30
|
+
|
31
|
+
# Access to the {Kangaroo::Util::Proxy::Common Common Proxy}
|
32
|
+
#
|
33
|
+
# @return common service proxy
|
34
|
+
def common
|
35
|
+
client.common
|
36
|
+
end
|
37
|
+
|
38
|
+
# Access to the {Kangaroo::Util::Proxy::Workflow Workflow Proxy}
|
39
|
+
#
|
40
|
+
# @return workflow service proxy
|
41
|
+
def workflow
|
42
|
+
@workflow_proxy ||= Proxy::Workflow.new client.object_service, db_name, user_id, password
|
43
|
+
end
|
44
|
+
|
45
|
+
# Access to the {Kangaroo::Util::Proxy::Object Object Proxy}
|
46
|
+
#
|
47
|
+
# @return object service proxy
|
48
|
+
def object model_name
|
49
|
+
Proxy::Object.new client.object_service, db_name, user_id, password, model_name
|
50
|
+
end
|
51
|
+
|
52
|
+
# Access to the {Kangaroo::Util::Proxy::Wizard Wizard Proxy}
|
53
|
+
#
|
54
|
+
# @return wizard service proxy
|
55
|
+
def wizard
|
56
|
+
@wizard_proxy ||= Proxy::Wizard.new client.wizard_service, db_name, user_id, password
|
57
|
+
end
|
58
|
+
|
59
|
+
# Access to the {Kangaroo::Util::Proxy::Report Report Proxy}
|
60
|
+
#
|
61
|
+
# @return report service proxy
|
62
|
+
def report
|
63
|
+
@report_proxy ||= Proxy::Report.new client.report_service, db_name, user_id, password
|
64
|
+
end
|
65
|
+
|
66
|
+
# Test of the current user is logged in
|
67
|
+
#
|
68
|
+
# @return true/false
|
69
|
+
def logged_in?
|
70
|
+
!!user_id
|
71
|
+
end
|
72
|
+
|
73
|
+
# Login the current user
|
74
|
+
#
|
75
|
+
# @return true/false
|
76
|
+
def login!
|
77
|
+
@user_id = common.login db_name, user, password
|
78
|
+
|
79
|
+
logged_in?
|
80
|
+
end
|
81
|
+
|
82
|
+
# Login the current user, unless logged in.
|
83
|
+
#
|
84
|
+
# @return true/false
|
85
|
+
def login
|
86
|
+
logged_in? || login!
|
87
|
+
rescue
|
88
|
+
false
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'active_support/core_ext/enumerable'
|
2
|
+
require 'kangaroo/ruby_adapter/base'
|
3
|
+
|
4
|
+
module Kangaroo
|
5
|
+
module Util
|
6
|
+
class Loader
|
7
|
+
autoload :Model, 'kangaroo/util/loader/model'
|
8
|
+
autoload :Namespace, 'kangaroo/util/loader/namespace'
|
9
|
+
attr_accessor :model_names, :models, :database, :namespace
|
10
|
+
|
11
|
+
# Initialize a Loader instance
|
12
|
+
#
|
13
|
+
# @param [Array] model_names List of model names / patterns to load
|
14
|
+
def initialize model_names, database, namespace = "Oo"
|
15
|
+
@namespace = namespace[0,2] == "::" ? namespace : "::#{namespace}"
|
16
|
+
@database = database
|
17
|
+
@model_names = model_names
|
18
|
+
sanitize_model_names
|
19
|
+
end
|
20
|
+
|
21
|
+
# Loads matching models and uses {Kangaroo::RubyAdapter::Base RubyAdapter} to
|
22
|
+
# create the neccessary Ruby classes.
|
23
|
+
#
|
24
|
+
# @return [Array] list of ruby models
|
25
|
+
def load!
|
26
|
+
load_oo_models
|
27
|
+
sort_oo_models
|
28
|
+
adapt_oo_models
|
29
|
+
end
|
30
|
+
|
31
|
+
protected
|
32
|
+
def root_module
|
33
|
+
namespace.constantize
|
34
|
+
|
35
|
+
rescue NameError
|
36
|
+
eval <<-RUBY
|
37
|
+
module #{namespace}
|
38
|
+
extend Kangaroo::Util::Loader::Namespace
|
39
|
+
end
|
40
|
+
RUBY
|
41
|
+
end
|
42
|
+
|
43
|
+
def ir_module
|
44
|
+
root_module.const_defined?("Ir") ?
|
45
|
+
root_module.const_get("Ir") :
|
46
|
+
root_module.const_set("Ir", Module.new)
|
47
|
+
end
|
48
|
+
|
49
|
+
def reflection_model
|
50
|
+
ir_module.const_defined?("Model") ?
|
51
|
+
ir_module.const_get("Model") :
|
52
|
+
ir_module.const_set("Model", create_reflection_model)
|
53
|
+
end
|
54
|
+
|
55
|
+
def create_reflection_model
|
56
|
+
Class.new(Kangaroo::Model::Base).tap do |model|
|
57
|
+
model.send :include, Model
|
58
|
+
model.database = database
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def load_oo_models
|
63
|
+
@models = model_names.sum([]) do |model_name|
|
64
|
+
reflection_model.where("model ilike #{model_name}").all
|
65
|
+
end.uniq
|
66
|
+
end
|
67
|
+
|
68
|
+
def sort_oo_models
|
69
|
+
@models = @models.sort_by &:length_of_model_name
|
70
|
+
end
|
71
|
+
|
72
|
+
def adapt_oo_models
|
73
|
+
@models.map do |model|
|
74
|
+
RubyAdapter::Base.new(model).to_ruby
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def sanitize_model_names
|
79
|
+
@model_names = case @model_names
|
80
|
+
when nil, []
|
81
|
+
raise 'No models specified.'
|
82
|
+
when :all
|
83
|
+
['%']
|
84
|
+
when Array
|
85
|
+
@model_names.map do |model_name|
|
86
|
+
replace_wildcard model_name
|
87
|
+
end
|
88
|
+
else
|
89
|
+
raise "Expected list of models or :all, got #{@model_names.inspect}"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def replace_wildcard string
|
94
|
+
string.gsub '*', '%'
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|