leap 0.5.5 → 0.5.6

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/README.markdown CHANGED
@@ -256,6 +256,34 @@ and then perform your decision with a protocol constraint:
256
256
 
257
257
  You can name your protocols how ever you want; they just have to match between the quorum assertions and the decision option.
258
258
 
259
+ ## Logging
260
+
261
+ You can follow along with Leap's computation process by enabling detailed logging with
262
+
263
+ ``` ruby
264
+ Leap.log!
265
+ ```
266
+
267
+ By default, after being enabled, Leap will log to `Logger.new($stdout)`, per Ruby convention. If you have custom logging needs, use
268
+
269
+ ``` ruby
270
+ Leap.log!(my_logging_object)
271
+ ```
272
+
273
+ Make sure your logging object (`my_logging_object` in this example) provides a `#info` method.
274
+
275
+ ## Instrumentation
276
+
277
+ You can time Leap activity by enabling instrumentation:
278
+
279
+ ``` ruby
280
+ Leap.instrument!
281
+ ```
282
+
283
+ Leap uses `Benchmark.measure` internally and displays timings in standard `Benchmark::Tms` format.
284
+
285
+ Enabling instrumentation automatically enables logging.
286
+
259
287
  ## Internals
260
288
 
261
289
  Normally you'll construct your decision strategies using `decide :foo . . . end` blocks and perform them using the resulting `#foo` methods, but sometimes you'll want access to Leap's internals.
@@ -46,12 +46,17 @@ module Leap
46
46
  # @see Leap::GoalMethodsDocumentation
47
47
  # @return Leap::Report
48
48
  def report(characteristics, considerations, options = {})
49
- quorums.grab do |quorum|
49
+ Leap.log.committee "Convening committee", name
50
+ quorums.each do |quorum|
51
+ Leap.log.quorum "Assessing quorum", quorum.name
50
52
  next unless quorum.satisfied_by? characteristics and quorum.complies_with? Array.wrap(options[:comply])
53
+ Leap.log.quorum "Acknowledging quorum", quorum.name
51
54
  if conclusion = quorum.acknowledge(characteristics.slice(*quorum.characteristics), considerations.dup)
52
- ::Leap::Report.new self, quorum => conclusion
55
+ Leap.log.quorum "Success", quorum.name
56
+ return ::Leap::Report.new(self, quorum, conclusion)
53
57
  end
54
58
  end
59
+ nil
55
60
  end
56
61
 
57
62
  include ::Blockenspiel::DSL
data/lib/leap/decision.rb CHANGED
@@ -33,11 +33,17 @@ module Leap
33
33
  # General you won't call this directly, but rather use the dynamically-created method with this decision's goal as its name on the subject instance.
34
34
  # @see Leap::GoalMethodsDocumentation
35
35
  def make(characteristics, *considerations)
36
+ Leap.log.decision "Leaping to conclusion", goal
37
+ Leap.log.decision "Initial characteristics: #{characteristics.inspect}", goal
36
38
  options = considerations.extract_options!
37
39
  committees.reject { |c| characteristics.keys.include? c.name }.reverse.inject(Deliberation.new(characteristics)) do |deliberation, committee|
38
- if report = committee.report(deliberation.characteristics, considerations, options)
39
- deliberation.reports.unshift report
40
- deliberation.characteristics[committee.name] = report.conclusion
40
+ Leap.instrument.committee committee.name do
41
+ if report = committee.report(deliberation.characteristics, considerations, options)
42
+ Leap.log.committee "Success", committee.name
43
+ deliberation.reports.unshift report
44
+ deliberation.characteristics[committee.name] = report.conclusion
45
+ end
46
+ Leap.log.decision "Updated characteristics: #{deliberation.characteristics.inspect}", goal
41
47
  end
42
48
  deliberation
43
49
  end
data/lib/leap/quorum.rb CHANGED
@@ -46,8 +46,10 @@ module Leap
46
46
  # @param [Hash] characteristics
47
47
  # @return The methodology's result
48
48
  def acknowledge(characteristics, considerations)
49
- considerations.unshift characteristics
50
- process.call(*considerations[0...process.arity])
49
+ Leap.instrument.quorum name do
50
+ considerations.unshift characteristics
51
+ process.call(*considerations[0...process.arity])
52
+ end
51
53
  end
52
54
 
53
55
  # All characteristics either needed or wanted by the quorum.
@@ -0,0 +1,68 @@
1
+ module Leap
2
+ require 'logger'
3
+
4
+ # Begin logging Leap activity
5
+ #
6
+ # @param [optional] logger An object to receive logging signals (currently <tt>#info</tt>). If not provided, Leap will log to <tt>Logger.new($stdout)</tt>.
7
+ def log!(logger = nil)
8
+ @@logger = Register.new logger
9
+ end
10
+ module_function :log!
11
+
12
+ # Returns the logger object if logging is enabled
13
+ def log?
14
+ defined?(@@logger)
15
+ end
16
+ module_function :log?
17
+
18
+ # Returns the logging object--or a "black hole" logger if logging is not enabled.
19
+ def log
20
+ log? ? @@logger : @@shredder ||= Shredder.new
21
+ end
22
+ module_function :log
23
+
24
+ # Facilitates the logging of Leap activity
25
+ class Register
26
+ # Creates a <tt>Leap::Register</tt> wrapper around a given (optional) logger. If no logger is provided, Leap assumes <tt>Logger.new($stdout)</tt>.
27
+ def initialize(logger = nil)
28
+ @logger = logger || ::Logger.new($stdout)
29
+ end
30
+
31
+ # Log a Leap decision
32
+ #
33
+ # @param [String] message The message to be logged
34
+ # @param [String] name The name of the decision
35
+ def decision(message, name)
36
+ record name, message, 0
37
+ end
38
+
39
+ # Log Leap committee action
40
+ #
41
+ # @param [String] message The message to be logged
42
+ # @param [String] name The name of the committee
43
+ def committee(message, name)
44
+ record name, message, 1
45
+ end
46
+
47
+ # Log Leap quorum activity
48
+ #
49
+ # @param [String] message The message to be logged
50
+ # @param [String] name The name of the quorum
51
+ def quorum(message, name)
52
+ record name, message, 2
53
+ end
54
+
55
+ private
56
+
57
+ def record(name, message, indents)
58
+ @logger.info 'Leap ' + (' ' * indents * 2) + " [#{name}] #{message}"
59
+ end
60
+ end
61
+
62
+ # A "black hole" logging class that does absolutely nothing with the logging messages it receives.
63
+ class Shredder
64
+ def decision(*) nil end
65
+ def committee(*) nil end
66
+ def quorum(*) nil end
67
+ end
68
+ end
data/lib/leap/report.rb CHANGED
@@ -15,13 +15,15 @@ module Leap
15
15
  #
16
16
  # This is generally called in the midst of <tt>Leap::Committee#report</tt>
17
17
  # @param [Leap::Committee] The committee that produced the report.
18
+ # @param [Leap::Quorum] The responsible quorum.
19
+ # @param [any] The conclusion.
18
20
  # @param [Hash] report A single-pair hash containing the responsible quorum and its conclusion.
19
- # @raise [ArgumentError] Raised for anonymous reports, or if the report is not made properly.
20
- def initialize(committee, report)
21
+ # @raise [ArgumentError] Raised for anonymous reports.
22
+ def initialize(committee, quorum, conclusion)
21
23
  raise ArgumentError, 'Reports must identify themselves' unless committee.is_a?(::Leap::Committee)
22
24
  @committee = committee
23
- raise ArgumentError, 'Please report with quorum => conclusion' unless report.is_a?(Hash) and report.length == 1
24
- @quorum, @conclusion = report.first
25
+ @quorum = quorum
26
+ @conclusion = conclusion
25
27
  end
26
28
  end
27
29
  end
data/lib/leap/subject.rb CHANGED
@@ -50,16 +50,20 @@ module Leap
50
50
  decisions[goal] = ::Leap::Decision.new goal, options
51
51
  Blockenspiel.invoke(blk, decisions[goal])
52
52
  define_method goal do |*considerations|
53
- @deliberations ||= {}
54
- decision = self.class.decisions[goal]
55
- characteristics = send(self.class.decisions[goal].signature_method)
56
- @deliberations[goal] = decision.make(characteristics, *considerations)
57
- if self.class.decisions[goal].mastered? and @deliberations[goal][goal].nil?
58
- raise ::Leap::NoSolutionError, :goal => goal, :deliberation => @deliberations[goal]
59
- elsif self.class.decisions[goal].mastered?
60
- @deliberations[goal][goal]
61
- else
62
- @deliberations[goal]
53
+ Leap.instrument.decision goal do
54
+ @deliberations ||= {}
55
+ decision = self.class.decisions[goal]
56
+ characteristics = send(decision.signature_method)
57
+ @deliberations[goal] = decision.make(characteristics, *considerations)
58
+ if decision.mastered? and @deliberations[goal][goal].nil?
59
+ raise ::Leap::NoSolutionError, :goal => goal, :deliberation => @deliberations[goal]
60
+ elsif decision.mastered?
61
+ Leap.log.decision "Success", goal
62
+ @deliberations[goal][goal]
63
+ else
64
+ Leap.log.decision "Success", goal
65
+ @deliberations[goal]
66
+ end
63
67
  end
64
68
  end
65
69
  end
data/lib/leap/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Leap
2
- VERSION = "0.5.5"
2
+ VERSION = "0.5.6"
3
3
  end
data/lib/leap/whip.rb ADDED
@@ -0,0 +1,62 @@
1
+ module Leap
2
+ require 'benchmark'
3
+
4
+ # Begin instrumenting Leap activity. Automatically enables logging.
5
+ def instrument!
6
+ Leap.log! unless Leap.log?
7
+ @@whip = Whip.new
8
+ end
9
+ module_function :instrument!
10
+
11
+ # Returns the instrumentation object if enabled
12
+ def instrument?
13
+ defined?(@@whip)
14
+ end
15
+ module_function :instrument?
16
+
17
+ # Returns the instrumentation object--or a "pass-through" substitute if not enabled
18
+ def instrument(&blk)
19
+ instrument? ? @@whip : @@bystander ||= Bystander.new
20
+ end
21
+ module_function :instrument
22
+
23
+ # Facilitates the instrumentation of Leap activity
24
+ class Whip
25
+ # Instrument a Leap decision
26
+ #
27
+ # @param [String, Symbol] name The decision's name
28
+ # @param [Proc] blk A proc wrapping the decision to instrument
29
+ def decision(name, &blk)
30
+ Leap.log.decision instrument(&blk), name
31
+ end
32
+
33
+ # Instrument Leap committee activity
34
+ #
35
+ # @param [String, Symbol] name The committee's name
36
+ # @param [Proc] blk A proc wrapping the committee convention to instrument
37
+ def committee(name, &blk)
38
+ Leap.log.committee instrument(&blk), name
39
+ end
40
+
41
+ # Instrument Leap quorum activity
42
+ #
43
+ # @param [String, Symbol] name The quorum's name
44
+ # @param [Proc] blk A proc wrapping the quorum activity to instrument
45
+ def quorum(name, &blk)
46
+ Leap.log.quorum instrument(&blk), name
47
+ end
48
+
49
+ private
50
+
51
+ def instrument(&blk)
52
+ 'Completed in ' + Benchmark.measure(&blk).format('%10.6r').gsub(/[()]/,'').strip + 's'
53
+ end
54
+ end
55
+
56
+ # Allows Leap activity to continue uninstrumented
57
+ class Bystander
58
+ def decision(_, &blk) yield end
59
+ def committee(_, &blk) yield end
60
+ def quorum(_, &blk) yield end
61
+ end
62
+ end
data/lib/leap.rb CHANGED
@@ -8,7 +8,6 @@ require 'active_support/version'
8
8
  end if ActiveSupport::VERSION::MAJOR == 3
9
9
  require 'blockenspiel'
10
10
 
11
- require 'leap/core_ext'
12
11
  require 'leap/subject'
13
12
  require 'leap/committee'
14
13
  require 'leap/quorum'
@@ -19,6 +18,8 @@ require 'leap/implicit_attributes'
19
18
  require 'leap/no_solution_error'
20
19
  require 'leap/deliberations_accessor'
21
20
  require 'leap/goal_methods_documentation'
21
+ require 'leap/register'
22
+ require 'leap/whip'
22
23
 
23
24
  # Leap is a system for: 1) describing decision-making strategies used to determine a potentially non-obvious attribute of an object and 2)
24
25
  # computing that attribute by choosing appropriate strategies given a specific set of input information
metadata CHANGED
@@ -1,137 +1,101 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: leap
3
- version: !ruby/object:Gem::Version
4
- hash: 1
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.5.6
5
5
  prerelease:
6
- segments:
7
- - 0
8
- - 5
9
- - 5
10
- version: 0.5.5
11
6
  platform: ruby
12
- authors:
7
+ authors:
13
8
  - Andy Rossmeissl
14
9
  - Seamus Abshere
15
10
  - Derek Kastner
16
11
  autorequire:
17
12
  bindir: bin
18
13
  cert_chain: []
19
-
20
- date: 2011-08-17 00:00:00 Z
21
- dependencies:
22
- - !ruby/object:Gem::Dependency
14
+ date: 2011-12-15 00:00:00.000000000 Z
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
23
17
  name: charisma
24
- version_requirements: &id001 !ruby/object:Gem::Requirement
18
+ requirement: &10759248 !ruby/object:Gem::Requirement
25
19
  none: false
26
- requirements:
20
+ requirements:
27
21
  - - ~>
28
- - !ruby/object:Gem::Version
29
- hash: 23
30
- segments:
31
- - 0
32
- - 2
33
- - 0
22
+ - !ruby/object:Gem::Version
34
23
  version: 0.2.0
35
- prerelease: false
36
24
  type: :development
37
- requirement: *id001
38
- - !ruby/object:Gem::Dependency
25
+ prerelease: false
26
+ version_requirements: *10759248
27
+ - !ruby/object:Gem::Dependency
39
28
  name: shoulda
40
- version_requirements: &id002 !ruby/object:Gem::Requirement
29
+ requirement: &10758996 !ruby/object:Gem::Requirement
41
30
  none: false
42
- requirements:
43
- - - ">="
44
- - !ruby/object:Gem::Version
45
- hash: 3
46
- segments:
47
- - 0
48
- version: "0"
49
- prerelease: false
31
+ requirements:
32
+ - - ! '>='
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
50
35
  type: :development
51
- requirement: *id002
52
- - !ruby/object:Gem::Dependency
36
+ prerelease: false
37
+ version_requirements: *10758996
38
+ - !ruby/object:Gem::Dependency
53
39
  name: bueller
54
- version_requirements: &id003 !ruby/object:Gem::Requirement
40
+ requirement: &10758720 !ruby/object:Gem::Requirement
55
41
  none: false
56
- requirements:
57
- - - ">="
58
- - !ruby/object:Gem::Version
59
- hash: 3
60
- segments:
61
- - 0
62
- version: "0"
63
- prerelease: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
64
46
  type: :development
65
- requirement: *id003
66
- - !ruby/object:Gem::Dependency
47
+ prerelease: false
48
+ version_requirements: *10758720
49
+ - !ruby/object:Gem::Dependency
67
50
  name: blockenspiel
68
- version_requirements: &id004 !ruby/object:Gem::Requirement
51
+ requirement: &10758420 !ruby/object:Gem::Requirement
69
52
  none: false
70
- requirements:
71
- - - ">="
72
- - !ruby/object:Gem::Version
73
- hash: 23
74
- segments:
75
- - 0
76
- - 3
77
- - 2
53
+ requirements:
54
+ - - ! '>='
55
+ - !ruby/object:Gem::Version
78
56
  version: 0.3.2
79
- prerelease: false
80
57
  type: :runtime
81
- requirement: *id004
82
- - !ruby/object:Gem::Dependency
58
+ prerelease: false
59
+ version_requirements: *10758420
60
+ - !ruby/object:Gem::Dependency
83
61
  name: activesupport
84
- version_requirements: &id005 !ruby/object:Gem::Requirement
62
+ requirement: &10758120 !ruby/object:Gem::Requirement
85
63
  none: false
86
- requirements:
87
- - - ">="
88
- - !ruby/object:Gem::Version
89
- hash: 11
90
- segments:
91
- - 2
92
- - 3
93
- - 4
64
+ requirements:
65
+ - - ! '>='
66
+ - !ruby/object:Gem::Version
94
67
  version: 2.3.4
95
- prerelease: false
96
68
  type: :runtime
97
- requirement: *id005
98
- - !ruby/object:Gem::Dependency
69
+ prerelease: false
70
+ version_requirements: *10758120
71
+ - !ruby/object:Gem::Dependency
99
72
  name: i18n
100
- version_requirements: &id006 !ruby/object:Gem::Requirement
73
+ requirement: &10757892 !ruby/object:Gem::Requirement
101
74
  none: false
102
- requirements:
103
- - - ">="
104
- - !ruby/object:Gem::Version
105
- hash: 3
106
- segments:
107
- - 0
108
- version: "0"
109
- prerelease: false
75
+ requirements:
76
+ - - ! '>='
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
110
79
  type: :runtime
111
- requirement: *id006
112
- - !ruby/object:Gem::Dependency
80
+ prerelease: false
81
+ version_requirements: *10757892
82
+ - !ruby/object:Gem::Dependency
113
83
  name: builder
114
- version_requirements: &id007 !ruby/object:Gem::Requirement
84
+ requirement: &10757616 !ruby/object:Gem::Requirement
115
85
  none: false
116
- requirements:
117
- - - ">="
118
- - !ruby/object:Gem::Version
119
- hash: 3
120
- segments:
121
- - 0
122
- version: "0"
123
- prerelease: false
86
+ requirements:
87
+ - - ! '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
124
90
  type: :runtime
125
- requirement: *id007
91
+ prerelease: false
92
+ version_requirements: *10757616
126
93
  description: Leap to conclusions
127
94
  email: andy@rossmeissl.net
128
95
  executables: []
129
-
130
96
  extensions: []
131
-
132
97
  extra_rdoc_files: []
133
-
134
- files:
98
+ files:
135
99
  - .document
136
100
  - .gitignore
137
101
  - Gemfile
@@ -140,7 +104,6 @@ files:
140
104
  - leap.gemspec
141
105
  - lib/leap.rb
142
106
  - lib/leap/committee.rb
143
- - lib/leap/core_ext.rb
144
107
  - lib/leap/decision.rb
145
108
  - lib/leap/deliberation.rb
146
109
  - lib/leap/deliberations_accessor.rb
@@ -148,43 +111,41 @@ files:
148
111
  - lib/leap/implicit_attributes.rb
149
112
  - lib/leap/no_solution_error.rb
150
113
  - lib/leap/quorum.rb
114
+ - lib/leap/register.rb
151
115
  - lib/leap/report.rb
152
116
  - lib/leap/subject.rb
153
117
  - lib/leap/version.rb
118
+ - lib/leap/whip.rb
154
119
  - test/helper.rb
155
120
  - test/test_leap.rb
156
121
  homepage: http://github.com/rossmeissl/leap
157
122
  licenses: []
158
-
159
123
  post_install_message:
160
124
  rdoc_options: []
161
-
162
- require_paths:
125
+ require_paths:
163
126
  - lib
164
- required_ruby_version: !ruby/object:Gem::Requirement
127
+ required_ruby_version: !ruby/object:Gem::Requirement
165
128
  none: false
166
- requirements:
167
- - - ">="
168
- - !ruby/object:Gem::Version
169
- hash: 3
170
- segments:
129
+ requirements:
130
+ - - ! '>='
131
+ - !ruby/object:Gem::Version
132
+ version: '0'
133
+ segments:
171
134
  - 0
172
- version: "0"
173
- required_rubygems_version: !ruby/object:Gem::Requirement
135
+ hash: 744383405
136
+ required_rubygems_version: !ruby/object:Gem::Requirement
174
137
  none: false
175
- requirements:
176
- - - ">="
177
- - !ruby/object:Gem::Version
178
- hash: 3
179
- segments:
138
+ requirements:
139
+ - - ! '>='
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ segments:
180
143
  - 0
181
- version: "0"
144
+ hash: 744383405
182
145
  requirements: []
183
-
184
146
  rubyforge_project:
185
- rubygems_version: 1.8.8
147
+ rubygems_version: 1.8.11
186
148
  signing_key:
187
149
  specification_version: 3
188
150
  summary: A heuristics engine for your Ruby objects
189
151
  test_files: []
190
-
data/lib/leap/core_ext.rb DELETED
@@ -1,11 +0,0 @@
1
- class Array
2
- # Like <tt>Array#detect</tt>, but returns the calculated value
3
- def grab(&blk)
4
- result = each do |element|
5
- value = yield element
6
- break value if value
7
- end
8
- return if result == self
9
- result
10
- end
11
- end