symbiont-ruby 0.2.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -2,8 +2,21 @@
2
2
 
3
3
  require 'bundler/gem_tasks'
4
4
  require 'rspec/core/rake_task'
5
+ require 'rubocop'
6
+ require 'rubocop/rake_task'
7
+ require 'rubocop-performance'
8
+ require 'rubocop-rspec'
9
+ require 'rubocop-rake'
5
10
  require 'yard'
6
11
 
12
+ RuboCop::RakeTask.new(:rubocop) do |t|
13
+ config_path = File.expand_path(File.join('.rubocop.yml'), __dir__)
14
+ t.options = ['--config', config_path]
15
+ t.requires << 'rubocop-rspec'
16
+ t.requires << 'rubocop-performance'
17
+ t.requires << 'rubocop-rake'
18
+ end
19
+
7
20
  RSpec::Core::RakeTask.new(:rspec)
8
21
 
9
22
  YARD::Rake::YardocTask.new(:doc) do |t|
@@ -13,6 +26,7 @@ end
13
26
 
14
27
  task default: :rspec
15
28
 
29
+ desc 'Code documentation coverage check'
16
30
  task yardoc: :doc do
17
31
  undocumented_code_objects = YARD::Registry.tap(&:load).select do |code_object|
18
32
  code_object.docstring.empty?
@@ -22,7 +36,7 @@ task yardoc: :doc do
22
36
  puts 'YARD COVERAGE [SUCCESS] => 100% documentation coverage!'
23
37
  else
24
38
  failing_code_objects = undocumented_code_objects.map do |code_object|
25
- "- #{code_object.class} => #{code_object.to_s}"
39
+ "- #{code_object.class} => #{code_object}"
26
40
  end.join("\n")
27
41
 
28
42
  abort("YARD COVERAGE [FAILURE] => No documentation found for: \n #{failing_code_objects}")
data/Steepfile ADDED
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ target :lib do
4
+ signature 'sig'
5
+ check 'lib'
6
+ end
data/bin/console CHANGED
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'bundler/setup'
4
- require 'symbiont/ruby'
5
+ require 'symbiont'
5
6
 
6
7
  require 'pry'
7
8
  Pry.start
data/lib/symbiont.rb CHANGED
@@ -9,6 +9,7 @@ module Symbiont
9
9
  require_relative 'symbiont/trigger'
10
10
  require_relative 'symbiont/public_trigger'
11
11
  require_relative 'symbiont/private_trigger'
12
+ require_relative 'symbiont/isolator'
12
13
  require_relative 'symbiont/executor'
13
14
  require_relative 'symbiont/context'
14
15
 
@@ -32,7 +32,8 @@ module Symbiont
32
32
  # @api public
33
33
  # @since 0.1.0
34
34
  def evaluate(*required_contexts, context_direction: Trigger::IOK, &closure)
35
- public_trigger(*required_contexts, context_direction: context_direction, &closure).__evaluate__
35
+ Isolator.new(default_direction: context_direction, &closure)
36
+ .evaluate(*required_contexts)
36
37
  end
37
38
 
38
39
  # Starts execution of a proc object in the context of the passed object with the selected
@@ -59,63 +60,8 @@ module Symbiont
59
60
  # @api public
60
61
  # @since 0.1.0
61
62
  def evaluate_private(*required_contexts, context_direction: Trigger::IOK, &closure)
62
- private_trigger(*required_contexts, context_direction: context_direction, &closure).__evaluate__
63
- end
64
-
65
- # Factory method that instantiates a public trigger with the desired execution context,
66
- # the direction of method dispatching and the closure that needs to be performed.
67
- #
68
- # @param required_contexts [Array<Object>]
69
- # A set of objects that should be used as the main context series for method resolving
70
- # algorithm.
71
- # @param context_direction [Array<Symbol>]
72
- # An array of symbols that represents the direction of contexts. Possible values:
73
- #
74
- # - Symbiont::IOK
75
- # - Symbiont::OIK
76
- # - Symbiont::OKI
77
- # - Symbiont::IKO
78
- # - Symbiont::KOI
79
- # - Symbiont::KIO
80
- # @param closure [Proc]
81
- # Proc object that will be evaluated in many contexts: initial, outer and kernel.
82
- # @return [Symbiont::PublicTrigger]
83
- #
84
- # @see Symbiont::PublicTrigger
85
- # @see Symbiont::Trigger
86
- #
87
- # @api public
88
- # @since 0.1.0
89
- def public_trigger(*required_contexts, context_direction: Trigger::IOK, &closure)
90
- PublicTrigger.new(*required_contexts, context_direction: context_direction, &closure)
91
- end
92
-
93
- # Factory method that instantiates a private trigger with the desired execution context,
94
- # the direction of method dispatching and the closure that needs to be performed.
95
- #
96
- # @param required_contexts [Array<Object>]
97
- # A set of objects that should be used as the main context series for method resolving
98
- # algorithm.
99
- # @param context_direction [Array<Symbol>]
100
- # An array of symbols that represents the direction of contexts. Possible values:
101
- #
102
- # - Symbiont::IOK
103
- # - Symbiont::OIK
104
- # - Symbiont::OKI
105
- # - Symbiont::IKO
106
- # - Symbiont::KOI
107
- # - Symbiont::KIO
108
- # @param closure [Proc]
109
- # Proc object that will be evaluated in many contexts: initial, outer and kernel.
110
- # @return [Symbiont::PrivateTrigger]
111
- #
112
- # @see Symbiont::PrivateTrigger
113
- # @see Symbiont::Trigger
114
- #
115
- # @api public
116
- # @since 0.1.0
117
- def private_trigger(*required_contexts, context_direction: Trigger::IOK, &closure)
118
- PrivateTrigger.new(*required_contexts, context_direction: context_direction, &closure)
63
+ Isolator.new(default_direction: context_direction, &closure)
64
+ .evaluate_private(*required_contexts)
119
65
  end
120
66
 
121
67
  # Gets the method object taken from the context that can respond to it.
@@ -142,7 +88,8 @@ module Symbiont
142
88
  # @api public
143
89
  # @since 0.1.0
144
90
  def public_method(method_name, *required_contexts, context_direction: Trigger::IOK, &closure)
145
- public_trigger(*required_contexts, context_direction: context_direction, &closure).method(method_name)
91
+ Isolator.new(default_direction: context_direction, &closure)
92
+ .public_method(method_name, *required_contexts)
146
93
  end
147
94
 
148
95
  # Gets the method object taken from the context that can respond to it.
@@ -169,7 +116,8 @@ module Symbiont
169
116
  # @api public
170
117
  # @since 0.1.0
171
118
  def private_method(method_name, *required_contexts, context_direction: Trigger::IOK, &closure)
172
- private_trigger(*required_contexts, context_direction: context_direction, &closure).method(method_name)
119
+ Isolator.new(default_direction: context_direction, &closure)
120
+ .private_method(method_name, *required_contexts)
173
121
  end
174
122
  end
175
123
  end
@@ -0,0 +1,185 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Symbiont
4
+ # Special object that wraps your proc object from any place and provides
5
+ # an ability to invoke this proc object lazily inside an any series of contexts.
6
+ #
7
+ # @api public
8
+ # @since 0.3.0
9
+ class Isolator
10
+ # Is raised when closure is not provided.
11
+ #
12
+ # @see #initialize
13
+ #
14
+ # @api public
15
+ # @since 0.3.0
16
+ UnprovidedClosureAttributeError = Class.new(ArgumentError)
17
+
18
+ # Proc object that will be evaluated in many contexts: initial, outer and kernel.
19
+ # Will be used as an outer-context for the method resolution.
20
+ #
21
+ # @return [Proc]
22
+ #
23
+ # @api public
24
+ # @since 0.3.0
25
+ attr_reader :closure
26
+
27
+ # An array of symbols that represents the direction of contexts. Used by default.
28
+ #
29
+ # @return [Array<Symbol>]
30
+ #
31
+ # @api public
32
+ # @since 0.3.0
33
+ attr_reader :default_direction
34
+
35
+ # Instantiates isolator object with corresponding default direction and closure.
36
+ #
37
+ # @option default_direction [Array<Symbol>]
38
+ # An array of symbols that represents the direction of contexts which is used as default
39
+ # context direction. Symbiont::Trigger::IOK is chosen by default.
40
+ # @param closure [Proc]
41
+ # Proc object that will be evaluated in many contexts: initial, outer and kernel.
42
+ # Will be used as an outer-context for the method resolution.
43
+ #
44
+ # @api public
45
+ # @since 0.3.0
46
+ def initialize(default_direction: Trigger::IOK, &closure)
47
+ raise UnprovidedClosureAttributeError, 'You should provide a closure' unless block_given?
48
+
49
+ @default_direction = default_direction
50
+ @closure = closure
51
+ end
52
+
53
+ # Starts execution of a proc object in the context of the passed object with the selected
54
+ # direction of method dispatching. Delegates execution to a public trigger.
55
+ #
56
+ # @param required_contexts [Array<Object>]
57
+ # A set of objects that should be used as the main context series for method resolving
58
+ # algorithm.
59
+ # @param direction [Array<Symbol>]
60
+ # An array of symbols that represents the direction of contexts.
61
+ # @return [void]
62
+ #
63
+ # @see Symbiont::Trigger#__evaluate__
64
+ # @see Symbiont::PublicTrigger
65
+ #
66
+ # @api public
67
+ # @since 0.3.0
68
+ def evaluate(*required_contexts, direction: default_direction)
69
+ public_trigger(*required_contexts, direction: direction).__evaluate__
70
+ end
71
+
72
+ # Starts execution of a proc object in the context of the passed object with the selected
73
+ # direction of method dispatching. Delegates execution to a private trigger.
74
+ #
75
+ # @param required_contexts [Array<Object>]
76
+ # A set of objects that should be used as the main context series for method resolving
77
+ # algorithm.
78
+ # @param direction [Array<Symbol>]
79
+ # An array of symbols that represents the direction of contexts.
80
+ # @return [void]
81
+ #
82
+ # @see Symbiont::Trigger#__evaluate__
83
+ # @see Symbiont::PrivateTrigger
84
+ #
85
+ # @api public
86
+ # @since 0.3.0
87
+ def evaluate_private(*required_contexts, direction: default_direction)
88
+ private_trigger(*required_contexts, direction: direction).__evaluate__
89
+ end
90
+
91
+ # Gets the method object taken from the context that can respond to it.
92
+ # Considers only public methods.
93
+ #
94
+ # @param method_name [Symbol,String] A name of required method.
95
+ # @param required_contexts [Array<Object>]
96
+ # A set of objects that should be used as the main context series for method resolving
97
+ # algorithm.
98
+ # @param direction [Array<Symbol>]
99
+ # An array of symbols that represents the direction of contexts.
100
+ # @return [Method]
101
+ #
102
+ # @see Symbiont::PublicTrigger
103
+ # @see Symbiont::Trigger#method
104
+ #
105
+ # @api public
106
+ # @since 0.3.0
107
+ def public_method(method_name, *required_contexts, direction: default_direction)
108
+ public_trigger(*required_contexts, direction: direction).method(method_name)
109
+ end
110
+
111
+ # Gets the method object taken from the context that can respond to it.
112
+ # Considers private methods and public methods.
113
+ #
114
+ # @param method_name [Symbol,String] A name of required method.
115
+ # @param required_contexts [Array<Object>]
116
+ # A set of objects that should be used as the main context series for method resolving
117
+ # algorithm.
118
+ # @param direction [Array<Symbol>]
119
+ # An array of symbols that represents the direction of contexts.
120
+ # @return [Method]
121
+ #
122
+ # @see Symbiont::PrivateTrigger
123
+ # @see Symbiont::Trigger#method
124
+ #
125
+ # @api public
126
+ # @since 0.3.0
127
+ def private_method(method_name, *required_contexts, direction: default_direction)
128
+ private_trigger(*required_contexts, direction: direction).method(method_name)
129
+ end
130
+
131
+ private
132
+
133
+ # Factory method that instantiates a public trigger with the desired execution context,
134
+ # the direction of method dispatching and the closure that needs to be performed.
135
+ #
136
+ # @param required_contexts [Array<Object>]
137
+ # A set of objects that should be used as the main context series for method resolving
138
+ # algorithm.
139
+ # @param direction [Array<Symbol>]
140
+ # An array of symbols that represents the direction of contexts. Possible values:
141
+ #
142
+ # - Symbiont::IOK
143
+ # - Symbiont::OIK
144
+ # - Symbiont::OKI
145
+ # - Symbiont::IKO
146
+ # - Symbiont::KOI
147
+ # - Symbiont::KIO
148
+ # @return [Symbiont::PublicTrigger]
149
+ #
150
+ # @see Symbiont::PublicTrigger
151
+ # @see Symbiont::Trigger
152
+ #
153
+ # @api private
154
+ # @since 0.3.0
155
+ def public_trigger(*required_contexts, direction: default_direction)
156
+ PublicTrigger.new(*required_contexts, context_direction: direction, &closure)
157
+ end
158
+
159
+ # Factory method that instantiates a private trigger with the desired execution context,
160
+ # the direction of method dispatching and the closure that needs to be performed.
161
+ #
162
+ # @param required_contexts [Array<Object>]
163
+ # A set of objects that should be used as the main context series for method resolving
164
+ # algorithm.
165
+ # @param direction [Array<Symbol>]
166
+ # An array of symbols that represents the direction of contexts. Possible values:
167
+ #
168
+ # - Symbiont::IOK
169
+ # - Symbiont::OIK
170
+ # - Symbiont::OKI
171
+ # - Symbiont::IKO
172
+ # - Symbiont::KOI
173
+ # - Symbiont::KIO
174
+ # @return [Symbiont::PrivateTrigger]
175
+ #
176
+ # @see Symbiont::PrivateTrigger
177
+ # @see Symbiont::Trigger
178
+ #
179
+ # @api private
180
+ # @since 0.3.0
181
+ def private_trigger(*required_contexts, direction: default_direction)
182
+ PrivateTrigger.new(*required_contexts, context_direction: direction, &closure)
183
+ end
184
+ end
185
+ end
@@ -17,7 +17,7 @@ module Symbiont
17
17
  #
18
18
  # @param method_name [String,Symbol] Method that a context should respond to.
19
19
  # @raise NoMethodError
20
- # Is raised when no one of the contexts are able to respond to the required method.
20
+ # Is raised when no one of the contexts are able to respond to the required method.
21
21
  # @return [Objcet]
22
22
  #
23
23
  # @see Symbiont::Trigger#__actual_context__
@@ -26,8 +26,47 @@ module Symbiont
26
26
  # @since 0.1.0
27
27
  def __actual_context__(method_name)
28
28
  __directed_contexts__.find do |context|
29
- context.respond_to?(method_name, true)
29
+ begin
30
+ context.respond_to?(method_name, true)
31
+ rescue ::NoMethodError
32
+ # NOTE:
33
+ # this situation is caused when the context object does not respodond to
34
+ # #resond_to? method (BasicObject instances for example)
35
+
36
+ context_singleton = __extract_singleton_class__(context)
37
+
38
+ context_singleton.private_instance_methods(true).include?(method_name) ||
39
+ context_singleton.instance_methods(true).include?(method_name)
40
+ end
30
41
  end || super
31
42
  end
43
+
44
+ # Returns a corresponding public/private method object of the actual context.
45
+ #
46
+ # @param method_name [String,Symbol] Method name
47
+ # @return [Method]
48
+ #
49
+ # @see [Symbiont::Trigger#method]
50
+ #
51
+ # @api private
52
+ # @since 0.5.0
53
+ def method(method_name)
54
+ __context__ = __actual_context__(method_name)
55
+
56
+ # NOTE:
57
+ # block is used cuz #__actual_context__can raise
58
+ # ::NoMethodError (ContextNoMethodError) too (and we should raise it).
59
+ begin
60
+ __context__.method(method_name)
61
+ rescue ::NoMethodError
62
+ # NOTE:
63
+ # this situation is caused when the context object does not respond
64
+ # to #method method (BasicObject instances for example). We can extract
65
+ # method objects via it's singleton class.
66
+
67
+ __context_singleton__ = __extract_singleton_class__(__context__)
68
+ __context_singleton__.instance_method(method_name).bind(__context__)
69
+ end
70
+ end
32
71
  end
33
72
  end
@@ -26,8 +26,47 @@ module Symbiont
26
26
  # @since 0.1.0
27
27
  def __actual_context__(method_name)
28
28
  __directed_contexts__.find do |context|
29
- context.respond_to?(method_name, false)
29
+ begin
30
+ context.respond_to?(method_name, false)
31
+ rescue ::NoMethodError
32
+ # NOTE:
33
+ # this situation is caused when the context object does not respodond to
34
+ # #resond_to? method (BasicObject instances for example)
35
+
36
+ context_singleton = __extract_singleton_class__(context)
37
+ context_singleton.public_instance_methods(true).include?(method_name)
38
+ end
30
39
  end || super
31
40
  end
41
+
42
+ # Returns a corresponding public method object of the actual context.
43
+ #
44
+ # @param method_name [String,Symbol] Method name
45
+ # @raise [::NameError]
46
+ # @raise [Symbiont::Trigger::ContextNoMethodError, ::NoMethodError]
47
+ # @return [Method]
48
+ #
49
+ # @see [Symbiont::Trigger#method]
50
+ #
51
+ # @api private
52
+ # @since 0.5.0
53
+ def method(method_name)
54
+ __context__ = __actual_context__(method_name)
55
+
56
+ # NOTE:
57
+ # block is used cuz #__actual_context__can raise
58
+ # ::NoMethodError (ContextNoMethodError) too (and we should raise it)
59
+ begin
60
+ __context__.method(method_name)
61
+ rescue ::NoMethodError
62
+ # NOTE:
63
+ # this situation is caused when the context object does not respond
64
+ # to #method method (BasicObject instances for example). We can extract
65
+ # method objects via it's singleton class.
66
+
67
+ __context_singleton__ = __extract_singleton_class__(__context__)
68
+ __context_singleton__.public_instance_method(method_name).bind(__context__)
69
+ end
70
+ end
32
71
  end
33
72
  end