hexx 5.4.0 → 6.0.0

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.
@@ -0,0 +1,51 @@
1
+ # encoding: utf-8
2
+
3
+ module Hexx
4
+
5
+ # Makes the class dependable from implementations through setter injections.
6
+ #
7
+ # Adds the private {#depends_on} helper method to the class.
8
+ # Basically the method is similar to the +attr_accessor+ with the following
9
+ # differencies:
10
+ #
11
+ # * it adds a semantics to the declaration.
12
+ # * it allows setting the default implementation.
13
+ # * dependency setters accepts classes and modules only and fails with
14
+ # the +TypeError+ otherwise.
15
+ # * dependency getters fails with the +NotImplemented+ error if the
16
+ # implementation hasn't been set.
17
+ # * if a default implementation is defined, the dependency cannot be
18
+ # set to +nil+.
19
+ #
20
+ # @example
21
+ # class MyClass
22
+ # extend Hexx::Dependable
23
+ #
24
+ # depends_on :another_class, default: AnotherClass
25
+ # depends_on :looks_for_implementation
26
+ # end
27
+ #
28
+ # object = MyClass.new
29
+ # object.another_class # => AnotherClass
30
+ #
31
+ # object.looks_for_implementation
32
+ # # => fails with NotImplementedError
33
+ #
34
+ # object.looks_for_implementation = SomeInjection
35
+ # object.looks_for_implementation # => SomeInjection
36
+ module Dependable
37
+
38
+ private
39
+
40
+ # @!method depends_on(name, options = {})
41
+ # Declares the dependency with its default implementation.
42
+ # @example (see Hexx::Dependable)
43
+ # @param [String, Symbol] name The name of the dependency.
44
+ # @param [Hash] options ({}) The dependency declaration options.
45
+ # @option options [String, Symbol, Class] :default (nil) Optional default
46
+ # implementation for the dependency.
47
+ def depends_on(name, default: nil)
48
+ Helpers::Dependency.add self, name, default
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,111 @@
1
+ # encoding: utf-8
2
+
3
+ module Hexx
4
+
5
+ # @api hide
6
+ # The module contains helper classes and modules.
7
+ #
8
+ # All the helpers injects some code into corresponding class or module.
9
+ #
10
+ # The content of the module is not a part of API, but its implementation
11
+ # details.
12
+ module Helpers
13
+
14
+ # @api hide
15
+ # @abstract
16
+ # The base class for attribute injection.
17
+ #
18
+ # The objects of the class validates its parameters and adds
19
+ # attribute's getter and setter into the target class or module.
20
+ class Base < Struct.new(:target, :name)
21
+
22
+ # @api hide
23
+ # @!method add(target, name)
24
+ # Injects an attribute to the target class.
25
+ #
26
+ # @param [Module] target The class or module to inject the attribute to.
27
+ # @param [String, Symbol] name The name of the attribute.
28
+ # @raise (see Hexx::Helpers::Base#validate)
29
+ # @return [Hexx::Helpers::Base] the base class object.
30
+ def self.add(*args)
31
+ new(*args).validate.add_getter.add_setter
32
+ end
33
+
34
+ # @api hide
35
+ # @!attribute target
36
+ # The class to inject the attribute to
37
+ # @return [Class] the target for the injection
38
+
39
+ # @api hide
40
+ # @!attribute name
41
+ # The name of the attribute to be created in a target class
42
+ # @return [Class] the name of the attribute
43
+
44
+ # @api hide
45
+ # Validates parameters of the dependency declaration.
46
+ #
47
+ # @raise (see Hexx::Helpers::Base#check_target)
48
+ # @raise (see Hexx::Helpers::Base#check_name_type)
49
+ # @raise (see Hexx::Helpers::Base#check_name_value)
50
+ # @return [Hexx::Helpers::Base] +self+
51
+ def validate
52
+ check_target
53
+ check_name_type
54
+ check_name_value
55
+ self
56
+ end
57
+
58
+ # @api hide
59
+ # Adds the parameter getter to the +target+ instance
60
+ # @return [Hexx::Helpers::Base] +self+
61
+ def add_getter
62
+ target.class_eval getter
63
+ self
64
+ end
65
+
66
+ # @api hide
67
+ # Adds the parameter setter to the +target+ instance
68
+ # @return [Hexx::Helpers::Base] +self+
69
+ def add_setter
70
+ target.class_eval setter
71
+ self
72
+ end
73
+
74
+ private
75
+
76
+ # @abstract
77
+ # @return [String] the getter definition
78
+ def getter
79
+ ""
80
+ end
81
+
82
+ # @abstract
83
+ # @return [String] the setter definition
84
+ def setter
85
+ ""
86
+ end
87
+
88
+ # @api hide
89
+ # @raise [TypeError] if a target to add the instance parameter to
90
+ # is not a class.
91
+ def check_target
92
+ return if target.is_a? Class
93
+ fail TypeError.new "#{ target.inspect } is not a class"
94
+ end
95
+
96
+ # @api hide
97
+ # @raise [TypeError] if the attribute name is neither a string nor symbol.
98
+ def check_name_type
99
+ return if name.is_a?(String) || name.is_a?(Symbol)
100
+ fail TypeError.new "#{ name.inspect } is neither string nor symbol"
101
+ end
102
+
103
+ # @api hide
104
+ # @raise [AttributeError] it the name is blank.
105
+ def check_name_value
106
+ return unless name.to_s == ""
107
+ fail ArgumentError.new "Dependency should have a name"
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,82 @@
1
+ # encoding: utf-8
2
+ require_relative "base"
3
+
4
+ module Hexx
5
+ module Helpers
6
+
7
+ # @api hide
8
+ # Coerces class attribute getter and setter with given type.
9
+ #
10
+ # @example
11
+ # Coersion.add MyClass, :name, Multibyte::Chars
12
+ # object = MyClass.new
13
+ #
14
+ # object.name = "Ivo"
15
+ # object.name
16
+ # # => #<Multibyte::Chars @wrapped_string="Ivo" >
17
+ class Coersion < Base
18
+
19
+ # @api hide
20
+ # @!scope class
21
+ # @!method add(target, name, type)
22
+ # Reloads the Base class initializer by adding the type of the attribute
23
+ # to coerce with.
24
+ #
25
+ # @example (see Hexx::Helpers::Coersion)
26
+ # @param (see Hexx::Helpers::Base.add)
27
+ # @param [Module] type The type of the attribute.
28
+ # @raise (see Hexx::Helpers::Coersion#validate)
29
+ # @return [Hexx::Helpers::Coersion] the coersion object.
30
+ def initialize(target, name, type)
31
+ super target, name
32
+ @type = type
33
+ end
34
+
35
+ # @!attribute type
36
+ # The type to coerce the attribute with
37
+ # @return [Class] the type of the attribute
38
+ attr_accessor :type
39
+
40
+ # @api hide
41
+ # Validates parameters of the coersion declaration.
42
+ #
43
+ # Adds validation of the coersion type to the
44
+ # {Hexx::Helper::Base#validate}.
45
+ #
46
+ # @raise (see Hexx::Helpers::Base.validate)
47
+ # @raise (see Hexx::Helpers::Coersion#check_type)
48
+ # @return [Hexx::Helpers::Coersion] +self+
49
+ def validate
50
+ check_type
51
+ super
52
+ end
53
+
54
+ private
55
+
56
+ # @api hide
57
+ # The definition of the coerced attribute getter.
58
+ # To be reloaded for ActiveRecord models.
59
+ def getter
60
+ "def #{ name }=(value);
61
+ @#{ name } = #{ type.name }.new(value);
62
+ end"
63
+ end
64
+
65
+ # @api hide
66
+ # The definition of the coerced attribute setter.
67
+ # To be reloaded for ActiveRecord models.
68
+ def setter
69
+ "def #{ name };
70
+ #{ type.name }.new(@#{ name });
71
+ end"
72
+ end
73
+
74
+ # @api hide
75
+ # @raise [TypeError] if type is not a class
76
+ def check_type
77
+ return if type.is_a? Class
78
+ fail TypeError.new "#{ type.inspect } is not a class"
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,87 @@
1
+ # encoding: utf-8
2
+ require_relative "base"
3
+
4
+ module Hexx
5
+ module Helpers
6
+
7
+ # @api hide
8
+ # @abstract
9
+ # The base class for both instance and module dependencies.
10
+ class Dependency < Base
11
+
12
+ # @api hide
13
+ # @!scope class
14
+ # @!method add(target, name, default)
15
+ # Reloads the Base class initializer by adding the default implementation
16
+ # for the dependency.
17
+ #
18
+ # @param (see Hexx::Helpers::Base)
19
+ # @param [Module] default The default implementation for the dependency.
20
+ # @raise (see Hexx::Helpers::Dependency#validate)
21
+ # @return [Hexx::Helpers::Dependency] the dependency object.
22
+ def initialize(target, name, default)
23
+ super target, name
24
+ @default = default
25
+ end
26
+
27
+ # @!attribute default
28
+ # The default implementation for the dependency
29
+ # @return [Module] the default implementation
30
+ attr_accessor :default
31
+
32
+ # @api hide
33
+ # Validates parameters for the dependency declaration.
34
+ #
35
+ # Adds validation of the dependency default implementation to the
36
+ # {Hexx::Helper::Base#validate}.
37
+ #
38
+ # @raise (see Hexx::Helpers::Base#validate)
39
+ # @raise (see Hexx::Helpers::Dependency#check_default)
40
+ # @return [Hexx::Helpers::Dependency] +self+
41
+ def validate
42
+ check_default
43
+ super
44
+ end
45
+
46
+ private
47
+
48
+ # @api hide
49
+ # The dependency getter
50
+ def getter
51
+ "def #{ name };
52
+ @#{ name } ||= #{ default_name };
53
+ return @#{ name } if @#{ name };
54
+ fail NotImplementedError
55
+ .new \"DI: #{ target.name }##{ name } not implemented\";
56
+ end"
57
+ end
58
+
59
+ # @api hide
60
+ # The dependency setter
61
+ def setter
62
+ "def #{ name }=(value);
63
+ if value.nil? || value.is_a?(Module);
64
+ @#{ name } = value;
65
+ #{ name };
66
+ else;
67
+ fail TypeError.new \"DI: value.inspect is not a module\";
68
+ end;
69
+ end"
70
+ end
71
+
72
+ # @api hide
73
+ # @raise [TypeError] if the default implementation is neither a class,
74
+ # nor module
75
+ def check_default
76
+ return if default.nil? || default.is_a?(Module)
77
+ fail TypeError.new "#{ default.inspect } is neither a class nor module"
78
+ end
79
+
80
+ # @api hide
81
+ # Returns the name of the default implementation of the dependency.
82
+ def default_name
83
+ @default_name ||= default ? default.name : "nil"
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,57 @@
1
+ # encoding: utf-8
2
+ require_relative "dependency"
3
+
4
+ module Hexx
5
+ module Helpers
6
+
7
+ # @api hide
8
+ # Module dependency constructor.
9
+ #
10
+ # Adds the dependency to selected module.
11
+ #
12
+ # @example
13
+ # ModuleDependency.add(MyModule, :some_module, DefaultImplementation)
14
+ #
15
+ # MyModule.some_module
16
+ # # => DefaultImplementation
17
+ #
18
+ # MyModule.some_module = AnotherImplementation
19
+ # # => AnotherImplementation
20
+ #
21
+ # MyModule.some_module = 1
22
+ # # fails with #<TypeError @message="1 is not a module" >
23
+ class ModuleDependency < Dependency
24
+
25
+ # @api hide
26
+ # Adds the dependency to given module
27
+ # @example (see Hexx::Helpers::ModuleDependency)
28
+ # @param [Module] target The module to declare the dependency of.
29
+ # @param [String, Symbol] name The name of the dependency.
30
+ # @param [Module] default The default implementation of the dependency.
31
+
32
+ # @api hide
33
+ # Adds the module dependency getter
34
+ # @return [ModuleDependency] +self+.
35
+ def add_getter
36
+ target.instance_eval getter
37
+ self
38
+ end
39
+
40
+ # @api hide
41
+ # Adds the module dependency setter
42
+ # @return [ModuleDependency] +self+.
43
+ def add_setter
44
+ target.instance_eval setter
45
+ self
46
+ end
47
+
48
+ private
49
+
50
+ # @raise [TypeError] if a target to add the dependency is not a module.
51
+ def check_target
52
+ return if target.is_a? Module
53
+ fail TypeError.new "#{ target.inspect } is not a module"
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,40 @@
1
+ # encoding: utf-8
2
+ require_relative "base"
3
+
4
+ module Hexx
5
+ module Helpers
6
+
7
+ # @api hide
8
+ # Adds setter and getter for the instance parameter.
9
+ #
10
+ # @example
11
+ # # Providing that MyClass#params returns a hash
12
+ # Parameter.add MyClass, :name
13
+ # object = MyClass.new
14
+ #
15
+ # object.params["name"] # => nil
16
+ # object.name # => nil
17
+ #
18
+ # object.params["name"] = "Ivan"
19
+ # object.params["name"] # => "Ivan"
20
+ # object.name # => "Ivan"
21
+ #
22
+ # object.name = "Ivo"
23
+ # object.params["name"] # => "Ivo"
24
+ # object.name # => "Ivo"
25
+ class Parameter < Base
26
+
27
+ private
28
+
29
+ # @api hide
30
+ def setter
31
+ "def #{ name }; params[\"#{ name }\"]; end"
32
+ end
33
+
34
+ # @api hide
35
+ def getter
36
+ "def #{ name }=(value); params[\"#{ name }\"] = value; end"
37
+ end
38
+ end
39
+ end
40
+ end
data/lib/hexx/service.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ require_relative "dependable"
2
3
 
3
4
  module Hexx
4
5
 
@@ -19,24 +20,99 @@ module Hexx
19
20
  # service.run
20
21
  # # => This will call the listener's method #on_found(item).
21
22
  class Service
23
+ extend Dependable
22
24
  include Wisper::Publisher
23
25
  include ActiveModel::Validations
24
- include Parameters
25
26
 
26
- # @!scope class
27
- # @!method new(options = {})
28
- # Constructs the service object.
27
+ # @api hide
28
+ # Returns the list of allowed parameters for service objects.
29
+ #
30
+ # The parameters are added to the list by the {.allow_params} private
31
+ # helper method.
29
32
  #
30
33
  # @example
31
- # service = Hexx::Service.new name: name
34
+ # class Service < Hexx::Service
35
+ # allow_params :name
36
+ # end
37
+ #
38
+ # Service.params # => "name"
39
+ #
40
+ # @return [Array<String>] The list of allowed instance parameters.
41
+ def self.params
42
+ @params ||= []
43
+ end
44
+
45
+ # @!scope class
46
+ # @!method validates(attribute, options)
47
+ # Adds a standard validation for the attribute.
48
+ # @param [Symbol, String] attribute The name of the attribute to validate.
49
+ # @param [Hash] options The list of validation options.
50
+ # @see ActiveModel validations {APIdocs}[
51
+ # http://apidock.com/rails/ActiveModel/Validations/ClassMethods/validates]
52
+
53
+ # @!scope class
54
+ # @!method validate(method, options)
55
+ # Adds a custom validation (calls given method).
56
+ # @param [Symbol, String] method The name of the validation method.
57
+ # @param [Hash] options The list of validation options.
58
+ # @see ActiveModel validations {APIdocs}[
59
+ # http://apidock.com/rails/ActiveModel/Validations/ClassMethods/validate]
60
+
61
+ # @!scope class
62
+ # @!method new(params = {})
63
+ # Constructs the service object with given parameters.
32
64
  #
33
- # @param [Hash] options The options to be assigned to the {#params}.
65
+ # @example (see Hexx::Service)
66
+ # @param [Hash] params ({}) The parameters of the service object to be
67
+ # assigned to the {#params} attribute.
34
68
  # @return [Hexx::Service] The service object.
35
- def initialize(options = {})
36
- super(options)
69
+
70
+ # @api hide
71
+ # Initializes the service object.
72
+ # @param (see Hexx::Service.new)
73
+ # @return (see Hexx::Service.new)
74
+ def initialize(params = {})
75
+ @params = params.dup.stringify_keys.slice(*(self.class.params))
37
76
  @messages = []
38
77
  end
39
78
 
79
+ # @!attribute [r] params
80
+ # The list of service object parameters.
81
+ #
82
+ # The attribute is assigned via the {.new} method options.
83
+ # On initialization the parameters (keys) are stringified and whitelisted.
84
+ #
85
+ # Allowed parameters should be explicitly declared with the {.allow_params}.
86
+ #
87
+ # @example Only whitelisted params are being assigned.
88
+ # class GetItem < Hexx::Service
89
+ # allow_params :name
90
+ # end
91
+ #
92
+ # service = GetItem.new name: "Олег", family: "Рюрикович"
93
+ # service.params # => { "name" => "Олег" }
94
+ # @return [Hash] the service object parameters.
95
+
96
+ # @!attribute [r] messages
97
+ # The array of service messages (instances of {Hexx::Service::Message})
98
+ # with +text+ and +type+ attributes.
99
+ #
100
+ # @example
101
+ # class Test < Hexx::Service
102
+ # def run
103
+ # add_message "info", :ok
104
+ # end
105
+ # end
106
+ #
107
+ # service = Test.new
108
+ # service.run # adds message
109
+ # service.messages
110
+ # # => [#<Hexx::Service::Message @text="ok" @type="info" >]
111
+ #
112
+ # @return [Array<Hexx::Service::Message>] The array of messages.
113
+
114
+ attr_reader :params, :messages
115
+
40
116
  # @!scope class
41
117
  # @!visibility private
42
118
  # @!method allow_params(*params)
@@ -54,28 +130,6 @@ module Hexx
54
130
  #
55
131
  # @param [Array<Symbol, String>] params The list of allowed keys.
56
132
 
57
- # @!scope class
58
- # @!method validates(attribute, options)
59
- # Adds a standard validation for the attribute.
60
- # @note The method is defined in the {ActiveModel::Validations} module.
61
-
62
- # @!scope class
63
- # @!method validate(method, options)
64
- # Adds a custom validation (calls given method).
65
- # @note The method is defined in the {ActiveModel::Validations} module.
66
-
67
- # @!attribute params [r] The list of service object parameters.
68
- # The attribute is assigned via the {.new} method options.
69
- # The keys should be explicitly declared by the {.allow_params} helper.
70
- #
71
- # @example Only whitelisted params are being assigned.
72
- # class GetItem < Hexx::Service
73
- # allow_params :name
74
- # end
75
- #
76
- # service = GetItem.new name: "Олег", family: "Рюрикович"
77
- # service.params # => { "name" => "Олег" }
78
-
79
133
  # @!method subscribe(listener, options = {})
80
134
  # Subscribes the listener to service object's notifications.
81
135
  # The <tt>:prefix</tt> sets the prefix to be added to a notification name
@@ -90,8 +144,8 @@ module Hexx
90
144
 
91
145
  # @abstract
92
146
  # Runs the service object.
93
- # @note The method does nothing and should be reloaded by a specific
94
- # service class.
147
+ #
148
+ # The method does nothing. To be reloaded by a specific service class.
95
149
  def run
96
150
  end
97
151
 
@@ -119,27 +173,18 @@ module Hexx
119
173
  WithCallbacks.new(self, prefix: prefix)
120
174
  end
121
175
 
122
- # @!attribute [r] messages
123
- # The array of service messages (instances of {Hexx::Service::Message})
124
- # with +text+ and +type+ attributes.
125
- #
126
- # @example
127
- # class Test < Hexx::Service
128
- # def run
129
- # add_message "info", :ok
130
- # end
131
- # end
132
- #
133
- # service = Test.new
134
- # service.run # adds message
135
- # service.messages
136
- # # => [#<Hexx::Service::Message @text="ok" @type="info" >]
137
- #
138
- # @return [Array<Hexx::Service::Message>] The array of messages.
139
- attr_reader :messages
140
-
141
176
  private
142
177
 
178
+ # Sets a list of allowed parameters for the class constructor and
179
+ # defines the corresponding instance attributes.
180
+ #
181
+ # @example (see Hexx::Service::Parameters.params)
182
+ # @param [Array<Symbol, String>] keys The list of allowed parameters.
183
+ def self.allow_params(*keys)
184
+ @params = keys.map(&:to_s)
185
+ params.each { |name| Helpers::Parameter.add self, name }
186
+ end
187
+
143
188
  # The helper runs another service object and subscribes +self+ for the
144
189
  # service object's notifications.
145
190
  #