wisper-visualize 0.0.1

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f7f18c7b6067cdf60a78ab594b64b7403883fdeb
4
+ data.tar.gz: 7625eb20cd5f4b9804e4790fd87f9dee086d028d
5
+ SHA512:
6
+ metadata.gz: a29965090d23ecfe01814dacad8e69e50c2c356f44f99e7cd2e20b89b478d49016d903bad0aa40d6a1c8e8e320e78cc1f5ccaa2ad337ab1c069585785d48163e
7
+ data.tar.gz: 020d16484de852eae21bd81e541c00934e540052f783f174dacd9193e482fc2ba82a573224c148c5025966e26c3938a1eb92854cd5f6a27a4942d6adb879b7cc
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ # gem 'wisper', path: '~/dev/wisper'
6
+
7
+ gem 'bundler'
8
+ gem 'rake'
9
+ gem 'rspec'
10
+ gem 'pry-byebug'
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Kris Leech
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,53 @@
1
+ # Wisper::Visualize
2
+
3
+ Provides a graph of published [Wisper](https://github.com/krisleech/wisper) events as a PNG or PDF
4
+
5
+ ![example output](https://raw.githubusercontent.com/krisleech/wisper-visualize/master/output_example.png)
6
+
7
+ ## Installation
8
+
9
+ ```ruby
10
+ gem 'wisper-visualize'
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ```ruby
16
+ report = Wisper::Visualize.report
17
+
18
+ # broadcast some events...
19
+
20
+ report.to_pdf('/tmp/events-graph.pdf')
21
+ report.to_png('/tmp/events-graph.png')
22
+ ```
23
+
24
+ You can also get a collection of all events published:
25
+
26
+ ```ruby
27
+ report.events # => [...]
28
+ ```
29
+
30
+ Each event has the following attributes:
31
+
32
+ ```ruby
33
+ event.name
34
+ event.publisher_name
35
+ event.subscriber_name
36
+ event.arg_names
37
+ event.created_at
38
+ event.frequency
39
+ ```
40
+
41
+ ## Rspec
42
+
43
+ ```ruby
44
+ around(:each) do |example|
45
+ report = Wisper::Visualize.report
46
+ example.call
47
+ report.to_pdf("/tmp/events-graph-#{example.metadata[:full_description]}.pdf")
48
+ end
49
+ ```
50
+
51
+ ## Contributing
52
+
53
+ Yes, please.
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,14 @@
1
+ require 'wisper'
2
+ require 'graphviz'
3
+
4
+ require 'wisper/visualize/recording_broadcaster'
5
+ require 'wisper/visualize/report'
6
+ require 'wisper/visualize/version'
7
+
8
+ module Wisper
9
+ module Visualize
10
+ def self.report
11
+ Report.new
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,80 @@
1
+ module Wisper
2
+ module Visualize
3
+ class RecordingBroadcaster
4
+ def initialize(broadcaster)
5
+ @events = []
6
+ @broadcaster = broadcaster
7
+ end
8
+
9
+ def broadcast(subscriber, publisher, event, args)
10
+ @broadcaster.broadcast(subscriber, publisher, event, args)
11
+ @events.push(Event.new(subscriber, publisher, event, args))
12
+ end
13
+
14
+ def events
15
+ @events.dup
16
+ end
17
+
18
+ # de-duplicates events and increments frequency for each duplicate
19
+ # FIXME: ugly...
20
+ def flattened_events
21
+ _result = []
22
+ _events = events
23
+
24
+ while event = _events.pop do
25
+ match = _events.find { |e| e == event }
26
+ if match.nil?
27
+ _result << event
28
+ else
29
+ match.increment_frequency
30
+ end
31
+ end
32
+
33
+ _result
34
+ end
35
+
36
+ class Event
37
+ attr_reader :subscriber_name, :publisher_name, :name, :arg_names,
38
+ :frequency, :created_at
39
+
40
+ def initialize(subscriber, publisher, name, args)
41
+ @subscriber_name = label(subscriber)
42
+ @publisher_name = label(publisher)
43
+ @name = name
44
+ @arg_names = arg_labels(args)
45
+ @frequency = 1
46
+ @created_at = Time.now
47
+ end
48
+
49
+ def frequency_label
50
+ frequency == 1 ? '' : "#{frequency}x "
51
+ end
52
+
53
+ def increment_frequency
54
+ @frequency += 1
55
+ end
56
+
57
+ def ==(other)
58
+ name == other.name &&
59
+ # arg_names == other.arg_names &&
60
+ subscriber_name == other.subscriber_name &&
61
+ publisher_name == other.publisher_name
62
+ end
63
+
64
+ private
65
+
66
+ def label(object)
67
+ id_method = %w(id uuid key object_id).find { |id_method| object.respond_to?(id_method) }
68
+ id = object.send(id_method)
69
+ class_name = object.class == Class ? object.name : object.class.name
70
+ "#{class_name}##{id}"
71
+ end
72
+
73
+ def arg_labels(args)
74
+ return 'no arguments' if args.empty?
75
+ args.map { |arg| label(arg) }.join(', ')
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,45 @@
1
+ module Wisper
2
+ module Visualize
3
+ class Report
4
+
5
+ def initialize
6
+ Wisper.configure do |config|
7
+ config.broadcaster :default, RecordingBroadcaster.new(config.broadcasters[:default])
8
+ end
9
+ end
10
+
11
+ def events
12
+ Wisper.configuration.broadcasters[:default].flattened_events
13
+ end
14
+
15
+ def to_pdf(path)
16
+ graph.output(pdf: path)
17
+ end
18
+
19
+ def to_png(path)
20
+ graph.output(png: path)
21
+ end
22
+
23
+ private
24
+
25
+ def graph
26
+ GraphViz.new(:G, type: :digraph, size: '500,500', ratio: 'auto').tap do |g|
27
+ events.each do |event|
28
+ sub_node = g.add_nodes(event.subscriber_name)
29
+ pub_node = g.add_nodes(event.publisher_name)
30
+
31
+ sub_node.shape = 'rect'
32
+ sub_node.color = 'steelblue'
33
+
34
+ pub_node.shape = 'rect'
35
+ pub_node.color = 'royalblue'
36
+
37
+ edge = g.add_edges(pub_node, sub_node, color: 'red')
38
+ edge[:label] = "#{event.frequency_label}#{event.name}"
39
+ # edge[:label] = "#{event.frequency_label}#{event.name}(#{event.arg_names})"
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,5 @@
1
+ module Wisper
2
+ module Visualize
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
Binary file
@@ -0,0 +1,24 @@
1
+ module Wisper::Visualize
2
+ describe RecordingBroadcaster do
3
+
4
+ subject(:broadcaster) { RecordingBroadcaster.new(another_broadcaster) }
5
+
6
+ let(:another_broadcaster) { double.as_null_object }
7
+
8
+ let(:publisher) { double }
9
+ let(:subscriber) { double }
10
+ let(:event) { 'foobar' }
11
+ let(:args) { [1,2,3] }
12
+
13
+ before { allow(subscriber).to receive(:foobar) }
14
+
15
+ it 'delegates broadcasting on to given broadcaster' do
16
+ expect(another_broadcaster).to receive(:broadcast).with(subscriber, publisher, event, args)
17
+ broadcaster.broadcast(subscriber, publisher, event, args)
18
+ end
19
+
20
+ it 'stores all events' do
21
+ expect { broadcaster.broadcast(subscriber, publisher, event, args) }.to change { broadcaster.events.size }.from(0).to(1)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,43 @@
1
+ module Wisper
2
+ module Visualize
3
+ describe Report do
4
+ let(:path) { '/tmp/visualization.pdf' }
5
+
6
+ let(:publisher_class) { Class.new { include Wisper::Publisher; public :broadcast } }
7
+
8
+ before do
9
+ File.delete(path) if File.exist?(path)
10
+ allow(publisher_class).to receive(:name).and_return('MyPublisher')
11
+ end
12
+
13
+ subject!(:report) { Report.new }
14
+
15
+ it '.to_pdf creates a file' do
16
+ publishers = 20.times.map { publisher_class.new }
17
+ subscribers = 20.times.map { double.as_null_object }
18
+ events = %w(user_created order_canceled order_created login_failed)
19
+
20
+ 40.times do
21
+ p = publishers.sample
22
+ s = subscribers.sample
23
+ p.subscribe(s)
24
+ p.broadcast(events.sample)
25
+ end
26
+
27
+ report.to_pdf(path)
28
+
29
+ expect(File.exist?(path)).to be_truthy
30
+ end
31
+
32
+ it '.events returns de-duplicated events' do
33
+ publisher = publisher_class.new
34
+ listener = double
35
+
36
+ allow(listener).to receive(:foobar)
37
+ publisher.subscribe(listener)
38
+
39
+ expect { 10.times { publisher.broadcast(:foobar) } }.to change { report.events.size }.from(0).to(1)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,28 @@
1
+ require 'wisper/visualize'
2
+
3
+ RSpec.configure do |config|
4
+ config.expect_with :rspec do |expectations|
5
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
6
+ end
7
+
8
+ config.mock_with :rspec do |mocks|
9
+ mocks.verify_partial_doubles = true
10
+ end
11
+
12
+ config.filter_run :focus
13
+ config.run_all_when_everything_filtered = true
14
+
15
+ # config.disable_monkey_patching!
16
+
17
+ config.warnings = true
18
+
19
+ if config.files_to_run.one?
20
+ config.default_formatter = 'doc'
21
+ end
22
+
23
+ config.profile_examples = 10
24
+
25
+ config.order = :random
26
+
27
+ Kernel.srand config.seed
28
+ end
@@ -0,0 +1,7 @@
1
+ module Wisper
2
+ describe Visualize do
3
+ it '.report return Report class' do
4
+ expect(Visualize.report).to be_an_instance_of(Visualize::Report)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'wisper/visualize/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "wisper-visualize"
8
+ spec.version = Wisper::Visualize::VERSION
9
+ spec.authors = ["Kris Leech"]
10
+ spec.email = ["kris.leech@gmail.com"]
11
+ spec.summary = 'Visualizations for Wisper events'
12
+ spec.description = 'Visualizations for Wisper events'
13
+ spec.homepage = "https://github.com/krisleech/wisper-visualize"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency 'wisper'
22
+ spec.add_dependency 'ruby-graphviz'
23
+ end
metadata ADDED
@@ -0,0 +1,92 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: wisper-visualize
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Kris Leech
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-10-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: wisper
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'
27
+ - !ruby/object:Gem::Dependency
28
+ name: ruby-graphviz
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: Visualizations for Wisper events
42
+ email:
43
+ - kris.leech@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - ".gitignore"
49
+ - ".rspec"
50
+ - Gemfile
51
+ - LICENSE.txt
52
+ - README.md
53
+ - Rakefile
54
+ - lib/wisper/visualize.rb
55
+ - lib/wisper/visualize/recording_broadcaster.rb
56
+ - lib/wisper/visualize/report.rb
57
+ - lib/wisper/visualize/version.rb
58
+ - output_example.png
59
+ - spec/recording_broadcaster_spec.rb
60
+ - spec/report_spec.rb
61
+ - spec/spec_helper.rb
62
+ - spec/visualize_spec.rb
63
+ - wisper-visualize.gemspec
64
+ homepage: https://github.com/krisleech/wisper-visualize
65
+ licenses:
66
+ - MIT
67
+ metadata: {}
68
+ post_install_message:
69
+ rdoc_options: []
70
+ require_paths:
71
+ - lib
72
+ required_ruby_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ required_rubygems_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ requirements: []
83
+ rubyforge_project:
84
+ rubygems_version: 2.2.2
85
+ signing_key:
86
+ specification_version: 4
87
+ summary: Visualizations for Wisper events
88
+ test_files:
89
+ - spec/recording_broadcaster_spec.rb
90
+ - spec/report_spec.rb
91
+ - spec/spec_helper.rb
92
+ - spec/visualize_spec.rb