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,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