advisor 0.1.0 → 0.2.0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: dce5c0242a387f50ef334a97a2583cade06759e3
4
- data.tar.gz: 3eccbc9c38091e8ab6eafd8f4a64f5fa2ecb07e7
3
+ metadata.gz: cc4b02e3e1fcc423abfbce5f70d121ac973ec5f4
4
+ data.tar.gz: b9f60ba8b0437012e6334eef615fa158a457f293
5
5
  SHA512:
6
- metadata.gz: 3d749fa9ae030a74fa61ee73b3a96cf739f48c0233ba110575453150d3adb73030a4522fb61d80bc1f1a00b189b6b0cc949f2d5e3120448767c9e9cf84311cc0
7
- data.tar.gz: 4427ed176fb927dda723ff41fdbc84ad07107dc02f5b255e6363177abd0f6fc207bf435b6591f68799d9ffb59d4c2f15e646a6a0d51845e1dcb4785dcad34958
6
+ metadata.gz: 7cf7cf08b21ec53349f10453f3be8653a8ec2c2e0cd2f03f7d014fa487410a52d882688e1a91eec2399be061baad8df5083e6fba3600371c44a209d9c35b8f11
7
+ data.tar.gz: a3fa5127caf63ac8259a74c45b6b3757e07a9998690e0ebb0688c0d47a6b0857405d0fade6e9f841f8a9c24e0f5f57c3264097d0a97501a8a490d8f50ab85c5d
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - '2.0.0'
4
+ - '2.1.0'
5
+ - '2.1.5'
6
+ - '2.2'
7
+ script: bundle exec rspec
data/Gemfile.lock CHANGED
@@ -1,7 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- advisor (0.1.0)
4
+ advisor (0.2.0)
5
+ metriks
5
6
 
6
7
  GEM
7
8
  remote: https://rubygems.org/
@@ -9,9 +10,17 @@ GEM
9
10
  ast (2.0.0)
10
11
  astrolabe (1.3.0)
11
12
  parser (>= 2.2.0.pre.3, < 3.0)
13
+ atomic (1.1.99)
14
+ avl_tree (1.2.1)
15
+ atomic (~> 1.1)
12
16
  coderay (1.1.0)
13
17
  diff-lcs (1.2.5)
18
+ hitimes (1.2.2)
14
19
  method_source (0.8.2)
20
+ metriks (0.9.9.7)
21
+ atomic (~> 1.0)
22
+ avl_tree (~> 1.2.0)
23
+ hitimes (~> 1.1)
15
24
  parser (2.2.0.3)
16
25
  ast (>= 1.1, < 3.0)
17
26
  powerpack (0.1.0)
data/Readme.org CHANGED
@@ -1,5 +1,7 @@
1
1
  * Advisor: Solve your cross-cutting concerns without mumbo-jumbo.
2
2
 
3
+ [[https://travis-ci.org/rranelli/advisor.svg?branch=master][https://travis-ci.org/rranelli/advisor.svg]]
4
+
3
5
  =Advisor= is Ruby gem that enables you to solve cross-cutting concerns without
4
6
  the usual =method-renaming= present in most alternatives.
5
7
 
data/advisor.gemspec CHANGED
@@ -18,6 +18,8 @@ Gem::Specification.new do |spec|
18
18
  spec.executables = spec.files.grep(%r{^bin\/}) { |f| File.basename(f) }
19
19
  spec.require_paths = ['lib']
20
20
 
21
+ spec.add_dependency 'metriks'
22
+
21
23
  spec.add_development_dependency 'bundler', '~> 1.7'
22
24
  spec.add_development_dependency 'rspec', '~> 3.0'
23
25
  spec.add_development_dependency 'rake', '~> 10.0'
@@ -0,0 +1,97 @@
1
+ require 'logger'
2
+ require 'metriks'
3
+
4
+ module Advisor
5
+ module Advices
6
+ class Metriks
7
+ class << self
8
+ attr_accessor :default_logger
9
+ end
10
+ self.default_logger = Logger.new(STDOUT)
11
+
12
+ def initialize(object, method, _call_args, **opts)
13
+ @object = object
14
+ @method = method
15
+
16
+ @instruments = Array(opts.fetch(:with)).uniq
17
+ @logger = opts[:logger] || Metriks.default_logger
18
+
19
+ fail 'No instruments defined' if instruments.empty?
20
+ fail 'Unknown Instrument' unless instruments.all?(&known_instrument?)
21
+ end
22
+
23
+ attr_reader :object, :method, :logger, :instruments
24
+
25
+ INSTRUMENTS = [
26
+ :counter, :timer, :gauge, :call_meter, :result_meter
27
+ ]
28
+
29
+ def self.applier_method
30
+ :measure
31
+ end
32
+
33
+ def call
34
+ (timed? ? timed_call { yield } : yield)
35
+ .tap { |result| instruments.each(&measure(result)) }
36
+ rescue => e
37
+ logger.warn(e)
38
+ raise
39
+ end
40
+
41
+ private
42
+
43
+ def timed_call
44
+ timer.time
45
+ yield.tap do
46
+ timer.stop
47
+ end
48
+ end
49
+
50
+ def measure(result)
51
+ # How I wish I had currying...
52
+ lambda do |instrument|
53
+ is_numeric = result.is_a?(Fixnum)
54
+
55
+ case instrument
56
+ when :counter then counter.increment
57
+ when :call_meter then call_meter.mark
58
+ when :result_meter then is_numeric && result_meter.mark(result)
59
+ when :gauge then is_numeric && gauge.set(result)
60
+ end
61
+ end
62
+ end
63
+
64
+ def timed?
65
+ instruments.include?(:timer)
66
+ end
67
+
68
+ def metric_prefix
69
+ "#{object.class}##{method}"
70
+ end
71
+
72
+ def timer
73
+ ::Metriks.timer("#{metric_prefix}_#{__callee__}")
74
+ end
75
+
76
+ def counter
77
+ ::Metriks.counter("#{metric_prefix}_#{__callee__}")
78
+ end
79
+
80
+ def gauge
81
+ ::Metriks.gauge("#{metric_prefix}_#{__callee__}")
82
+ end
83
+
84
+ def call_meter
85
+ ::Metriks.meter("#{metric_prefix}_#{__callee__}")
86
+ end
87
+
88
+ def result_meter
89
+ ::Metriks.meter("#{metric_prefix}_#{__callee__}")
90
+ end
91
+
92
+ def known_instrument?
93
+ -> (instrument) { INSTRUMENTS.include?(instrument) }
94
+ end
95
+ end
96
+ end
97
+ end
@@ -1 +1,2 @@
1
1
  require_relative 'advices/call_logger'
2
+ require_relative 'advices/metriks'
@@ -2,4 +2,5 @@ require 'logger'
2
2
 
3
3
  module Advisor
4
4
  Loggable = Factory.new(Advices::CallLogger).build
5
+ Measurable = Factory.new(Advices::Metriks).build
5
6
  end
@@ -4,6 +4,10 @@ module Advisor
4
4
  @advice_klass = advice_klass
5
5
  end
6
6
 
7
+ def self.build(advice_klass)
8
+ new(advice_klass).build
9
+ end
10
+
7
11
  def build
8
12
  advice_klazz = advice_klass
9
13
  advisor_module = method(:advisor_module)
@@ -1,3 +1,3 @@
1
1
  module Advisor
2
- VERSION = '0.1.0'
2
+ VERSION = '0.2.0.pre'
3
3
  end
@@ -12,7 +12,7 @@ module Advisor
12
12
  let(:args) { ['the universe', 'and everything'] }
13
13
  let(:logger) { instance_double(Logger) }
14
14
 
15
- let(:block) { -> () { :bla } }
15
+ let(:block) { -> { :bla } }
16
16
 
17
17
  describe '#call' do
18
18
  subject(:call) { advice.call(&block) }
@@ -0,0 +1,178 @@
1
+ require 'ostruct'
2
+
3
+ module Advisor
4
+ module Advices
5
+ describe Metriks do
6
+ subject(:advice) do
7
+ described_class.new(
8
+ object,
9
+ method,
10
+ args,
11
+ logger: logger,
12
+ with: instruments
13
+ )
14
+ end
15
+
16
+ let(:object) { OpenStruct.new(when: '2015-12-18') }
17
+ let(:method) { 'the_force_awakens' }
18
+ let(:args) { ['vai ser zika', 'demais'] }
19
+ let(:logger) { instance_double(Logger, warn: nil) }
20
+
21
+ let(:block) { -> { 42 } }
22
+
23
+ context 'when no instruments are specified' do
24
+ let(:instruments) { [] }
25
+
26
+ it { expect { call }.to raise_error }
27
+ end
28
+
29
+ describe '.applier_method' do
30
+ subject { Metriks.applier_method }
31
+ it { is_expected.to eq(:measure) }
32
+ end
33
+
34
+ describe '#call' do
35
+ subject(:call) { advice.call(&block) }
36
+
37
+ let(:instruments) { %i(counter result_meter call_meter gauge) }
38
+
39
+ shared_examples_for 'instruments && measuring' do
40
+ it('returns the block call value') { is_expected.to eq(42) }
41
+
42
+ it 'instantiates a counter with the right metric name' do
43
+ expect(::Metriks).to receive(:counter)
44
+ .with('OpenStruct#the_force_awakens_counter')
45
+ .and_call_original
46
+
47
+ subject
48
+ end
49
+
50
+ it 'instantiates gauge with the right metric name' do
51
+ expect(::Metriks).to receive(:gauge)
52
+ .with('OpenStruct#the_force_awakens_gauge')
53
+ .and_call_original
54
+
55
+ subject
56
+ end
57
+
58
+ it 'instantiates a method call and a meter with the right metric names' do
59
+ expect(::Metriks).to receive(:meter)
60
+ .with('OpenStruct#the_force_awakens_call_meter')
61
+ .and_call_original
62
+
63
+ expect(::Metriks).to receive(:meter)
64
+ .with('OpenStruct#the_force_awakens_result_meter')
65
+ .and_call_original
66
+
67
+ subject
68
+ end
69
+
70
+ let(:result_meter) { instance_double(::Metriks::Meter) }
71
+ let(:call_meter) { instance_double(::Metriks::Meter) }
72
+
73
+ it 'increments a method call counter' do
74
+ expect(::Metriks).to receive_message_chain(
75
+ :counter, :increment
76
+ )
77
+
78
+ subject
79
+ end
80
+
81
+ it 'marks a method call and a block result value meter' do
82
+ expect(::Metriks).to receive(:meter)
83
+ .with('OpenStruct#the_force_awakens_result_meter')
84
+ .and_return(result_meter)
85
+
86
+ expect(result_meter).to receive(:mark)
87
+ .with(42)
88
+
89
+ expect(::Metriks).to receive(:meter)
90
+ .with('OpenStruct#the_force_awakens_call_meter')
91
+ .and_return(call_meter)
92
+
93
+ expect(call_meter).to receive(:mark)
94
+
95
+ subject
96
+ end
97
+
98
+ it 'sets the block result value gauge' do
99
+ expect(::Metriks).to receive_message_chain(
100
+ :gauge, :set
101
+ ).with(42)
102
+
103
+ subject
104
+ end
105
+
106
+ context 'when the block return value is not numeric' do
107
+ let(:block) { -> { :war_in_the_stars } }
108
+
109
+ it 'does not instantiate a block return value meter' do
110
+ expect(::Metriks).to receive(:meter)
111
+ .with('OpenStruct#the_force_awakens_call_meter')
112
+ .and_return(call_meter)
113
+ expect(call_meter).to receive(:mark)
114
+
115
+ expect(::Metriks).not_to receive(:meter)
116
+ .with('OpenStruct#the_force_awakens_result_meter')
117
+
118
+ subject
119
+ end
120
+
121
+ it 'does not instantiate a block return value gauge' do
122
+ expect(::Metriks).not_to receive(:gauge)
123
+
124
+ subject
125
+ end
126
+
127
+ it 'instantiates a method call meter and marks it' do
128
+ expect(::Metriks).to receive(:counter)
129
+ .and_call_original
130
+
131
+ subject
132
+ end
133
+ end
134
+ end
135
+
136
+ it_behaves_like 'instruments && measuring'
137
+
138
+ context 'when using a timer' do
139
+ let(:instruments) { %i(counter result_meter call_meter gauge timer) }
140
+
141
+ let(:timer) { instance_double(::Metriks::Timer) }
142
+
143
+ before do
144
+ allow(::Metriks).to receive(:timer)
145
+ .and_return(timer)
146
+
147
+ allow(timer).to receive(:time)
148
+ allow(timer).to receive(:stop)
149
+ end
150
+
151
+ it_behaves_like 'instruments && measuring'
152
+
153
+ it 'finds the right timer metric' do
154
+ expect(::Metriks).to receive(:timer)
155
+ .with('OpenStruct#the_force_awakens_timer')
156
+
157
+ call
158
+ end
159
+
160
+ it 'times the method call' do
161
+ expect(timer).to receive(:time)
162
+ expect(timer).to receive(:stop)
163
+
164
+ call
165
+ end
166
+ end
167
+
168
+ context 'when there are duplicate instruments' do
169
+ let(:instruments) do
170
+ %i(counter result_meter call_meter counter gauge gauge)
171
+ end
172
+
173
+ it_behaves_like 'instruments && measuring'
174
+ end
175
+ end
176
+ end
177
+ end
178
+ end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: advisor
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0.pre
5
5
  platform: ruby
6
6
  authors:
7
7
  - Renan Ranelli
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-04-12 00:00:00.000000000 Z
11
+ date: 2015-04-24 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: metriks
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: bundler
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -91,6 +105,7 @@ files:
91
105
  - ".rspec"
92
106
  - ".rubocop.yml"
93
107
  - ".ruby-version"
108
+ - ".travis.yml"
94
109
  - Gemfile
95
110
  - Gemfile.lock
96
111
  - Rakefile
@@ -99,10 +114,12 @@ files:
99
114
  - lib/advisor.rb
100
115
  - lib/advisor/advices.rb
101
116
  - lib/advisor/advices/call_logger.rb
117
+ - lib/advisor/advices/metriks.rb
102
118
  - lib/advisor/builtin_advisors.rb
103
119
  - lib/advisor/factory.rb
104
120
  - lib/advisor/version.rb
105
121
  - spec/advisor/advices/call_logger_spec.rb
122
+ - spec/advisor/advices/metriks_spec.rb
106
123
  - spec/advisor/builtin_advisors_spec.rb
107
124
  - spec/advisor/factory_spec.rb
108
125
  - spec/spec_helper.rb
@@ -121,9 +138,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
121
138
  version: '2.0'
122
139
  required_rubygems_version: !ruby/object:Gem::Requirement
123
140
  requirements:
124
- - - ">="
141
+ - - ">"
125
142
  - !ruby/object:Gem::Version
126
- version: '0'
143
+ version: 1.3.1
127
144
  requirements: []
128
145
  rubyforge_project:
129
146
  rubygems_version: 2.2.2