advisor 0.1.0 → 0.2.0.pre

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