leap 0.5.5 → 0.5.6

Sign up to get free protection for your applications and to get access to all the features.
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