journeyviz 0.1.0
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 +7 -0
- data/.github/workflows/gem-push.yml +30 -0
- data/.github/workflows/tests.yml +39 -0
- data/.gitignore +11 -0
- data/.rspec +3 -0
- data/.rubocop.yml +35 -0
- data/.travis.yml +7 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +65 -0
- data/LICENSE.txt +21 -0
- data/README.md +99 -0
- data/Rakefile +8 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/journeyviz.gemspec +38 -0
- data/lib/journeyviz.rb +48 -0
- data/lib/journeyviz/action.rb +44 -0
- data/lib/journeyviz/block.rb +20 -0
- data/lib/journeyviz/graphable.rb +64 -0
- data/lib/journeyviz/graphable/externals.rb +73 -0
- data/lib/journeyviz/graphable/transitions.rb +67 -0
- data/lib/journeyviz/journey.rb +33 -0
- data/lib/journeyviz/node_group.rb +73 -0
- data/lib/journeyviz/normalized_name.rb +17 -0
- data/lib/journeyviz/scopable.rb +36 -0
- data/lib/journeyviz/screen.rb +29 -0
- data/lib/journeyviz/server.rb +17 -0
- data/lib/journeyviz/server/index.html.erb +100 -0
- data/lib/journeyviz/version.rb +5 -0
- metadata +172 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 00ee3821859c87a8c2a049d1ff60258ad6a5dfcd82e24ce3a8dd71dba9c1eead
|
4
|
+
data.tar.gz: ab015af88e3d0fb7de33f0f6a9a2b427f1a87ddc8ab034dfce18438a1d0a8298
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: fb8ac7208e4bddc29cf4b7567d74e5abb2a5bfa33e13421c5eac58bedee9637a600eecfec1eae7229ded8a7530eb0a84a7fed14146c53f6d686eb9eb07a50e90
|
7
|
+
data.tar.gz: ee35eadfa9a2ab5f8e94f454661beda75972b1c95b809e412d776402b8103debb3f11026975e0bf780c3531e44127e1dea295083b241160d6c18a483a2789054
|
@@ -0,0 +1,30 @@
|
|
1
|
+
name: Ruby Gem
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches: [ master ]
|
6
|
+
pull_request:
|
7
|
+
branches: [ master ]
|
8
|
+
|
9
|
+
jobs:
|
10
|
+
build:
|
11
|
+
name: Build + Publish
|
12
|
+
runs-on: ubuntu-latest
|
13
|
+
|
14
|
+
steps:
|
15
|
+
- uses: actions/checkout@v2
|
16
|
+
- name: Set up Ruby 2.6
|
17
|
+
uses: actions/setup-ruby@v1
|
18
|
+
with:
|
19
|
+
version: 2.6.x
|
20
|
+
|
21
|
+
- name: Publish to RubyGems
|
22
|
+
run: |
|
23
|
+
mkdir -p $HOME/.gem
|
24
|
+
touch $HOME/.gem/credentials
|
25
|
+
chmod 0600 $HOME/.gem/credentials
|
26
|
+
printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
|
27
|
+
gem build *.gemspec
|
28
|
+
gem push *.gem
|
29
|
+
env:
|
30
|
+
GEM_HOST_API_KEY: ${{secrets.RUBYGEMS_AUTH_TOKEN}}
|
@@ -0,0 +1,39 @@
|
|
1
|
+
name: CI
|
2
|
+
|
3
|
+
on: [push, pull_request]
|
4
|
+
|
5
|
+
jobs:
|
6
|
+
main:
|
7
|
+
name: >-
|
8
|
+
${{ matrix.os }} ${{ matrix.ruby }}
|
9
|
+
runs-on: ${{ matrix.os }}-latest
|
10
|
+
strategy:
|
11
|
+
fail-fast: false
|
12
|
+
matrix:
|
13
|
+
os: [ ubuntu, macos, windows ]
|
14
|
+
ruby: [ 2.4, 2.5, 2.6, 2.7, head ]
|
15
|
+
include:
|
16
|
+
- { os: windows, ruby: mingw }
|
17
|
+
exclude:
|
18
|
+
- { os: windows, ruby: head }
|
19
|
+
|
20
|
+
steps:
|
21
|
+
- name: windows misc
|
22
|
+
if: matrix.os == 'windows'
|
23
|
+
run: |
|
24
|
+
# set TMPDIR, git core.autocrlf
|
25
|
+
echo "::set-env name=TMPDIR::$env:RUNNER_TEMP"
|
26
|
+
git config --system core.autocrlf false
|
27
|
+
- name: checkout
|
28
|
+
uses: actions/checkout@v2
|
29
|
+
- name: set up Ruby
|
30
|
+
uses: ruby/setup-ruby@v1
|
31
|
+
with:
|
32
|
+
ruby-version: ${{ matrix.ruby }}
|
33
|
+
|
34
|
+
- name: install dependencies
|
35
|
+
run: bundle install --jobs 3 --retry 3
|
36
|
+
- name: spec
|
37
|
+
run: bundle exec rspec
|
38
|
+
- name: linter
|
39
|
+
run: bundle exec rubocop
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
Layout/LineLength:
|
2
|
+
Max: 120
|
3
|
+
|
4
|
+
# TODO: Enable documentation
|
5
|
+
Style/Documentation:
|
6
|
+
Enabled: false
|
7
|
+
|
8
|
+
Lint/RaiseException:
|
9
|
+
Enabled: true
|
10
|
+
|
11
|
+
Lint/StructNewOverride:
|
12
|
+
Enabled: true
|
13
|
+
|
14
|
+
Style/HashEachMethods:
|
15
|
+
Enabled: true
|
16
|
+
|
17
|
+
Style/HashTransformKeys:
|
18
|
+
Enabled: true
|
19
|
+
|
20
|
+
Style/HashTransformValues:
|
21
|
+
Enabled: true
|
22
|
+
|
23
|
+
Style/AsciiComments:
|
24
|
+
Enabled: false
|
25
|
+
|
26
|
+
Metrics/BlockLength:
|
27
|
+
Exclude:
|
28
|
+
- spec/**/*
|
29
|
+
|
30
|
+
Lint/AmbiguousBlockAssociation:
|
31
|
+
Exclude:
|
32
|
+
- spec/**/*
|
33
|
+
|
34
|
+
Layout/EndOfLine:
|
35
|
+
EnforcedStyle: lf
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
journeyviz (0.1.0)
|
5
|
+
rack-app
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
ast (2.4.0)
|
11
|
+
byebug (11.1.3)
|
12
|
+
diff-lcs (1.3)
|
13
|
+
docile (1.3.2)
|
14
|
+
jaro_winkler (1.5.4)
|
15
|
+
parallel (1.19.1)
|
16
|
+
parser (2.7.1.0)
|
17
|
+
ast (~> 2.4.0)
|
18
|
+
rack (2.2.3)
|
19
|
+
rack-app (7.6.4)
|
20
|
+
rack
|
21
|
+
rainbow (3.0.0)
|
22
|
+
rake (13.0.1)
|
23
|
+
rexml (3.2.4)
|
24
|
+
rspec (3.9.0)
|
25
|
+
rspec-core (~> 3.9.0)
|
26
|
+
rspec-expectations (~> 3.9.0)
|
27
|
+
rspec-mocks (~> 3.9.0)
|
28
|
+
rspec-core (3.9.1)
|
29
|
+
rspec-support (~> 3.9.1)
|
30
|
+
rspec-expectations (3.9.1)
|
31
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
32
|
+
rspec-support (~> 3.9.0)
|
33
|
+
rspec-mocks (3.9.1)
|
34
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
35
|
+
rspec-support (~> 3.9.0)
|
36
|
+
rspec-support (3.9.2)
|
37
|
+
rubocop (0.81.0)
|
38
|
+
jaro_winkler (~> 1.5.1)
|
39
|
+
parallel (~> 1.10)
|
40
|
+
parser (>= 2.7.0.1)
|
41
|
+
rainbow (>= 2.2.2, < 4.0)
|
42
|
+
rexml
|
43
|
+
ruby-progressbar (~> 1.7)
|
44
|
+
unicode-display_width (>= 1.4.0, < 2.0)
|
45
|
+
ruby-progressbar (1.10.1)
|
46
|
+
simplecov (0.18.5)
|
47
|
+
docile (~> 1.1)
|
48
|
+
simplecov-html (~> 0.11)
|
49
|
+
simplecov-html (0.12.2)
|
50
|
+
unicode-display_width (1.7.0)
|
51
|
+
|
52
|
+
PLATFORMS
|
53
|
+
ruby
|
54
|
+
|
55
|
+
DEPENDENCIES
|
56
|
+
bundler (~> 2.0)
|
57
|
+
byebug
|
58
|
+
journeyviz!
|
59
|
+
rake (~> 13.0)
|
60
|
+
rspec (~> 3.0)
|
61
|
+
rubocop (~> 0.81.0)
|
62
|
+
simplecov
|
63
|
+
|
64
|
+
BUNDLED WITH
|
65
|
+
2.0.2
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2020 Gabriel Teles
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
# Journeyviz
|
2
|
+
|
3
|
+
Journeyviz is a journey visualization gem. It defines journey as code so it can be versioned together with changes.
|
4
|
+
|
5
|
+
When defined, you can view your journey through a [rack](https://github.com/rack/rack) application.
|
6
|
+
|
7
|
+
Supports Ruby 2.6.x officially.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
gem 'journeyviz'
|
15
|
+
```
|
16
|
+
|
17
|
+
And then execute:
|
18
|
+
|
19
|
+
$ bundle
|
20
|
+
|
21
|
+
Or install it yourself as:
|
22
|
+
|
23
|
+
$ gem install journeyviz
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
|
28
|
+
### Defining the journey
|
29
|
+
|
30
|
+
Start by defining a journey. It is consisted of for elements: blocks, screens, actions and transitions.
|
31
|
+
|
32
|
+
Let's start from screens:
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
# This is the journey definition block
|
36
|
+
Journeyviz.configure do |journey|
|
37
|
+
# Here we're defining a screen called landing page
|
38
|
+
journey.screen :landing_page
|
39
|
+
end
|
40
|
+
```
|
41
|
+
|
42
|
+
Now supose our landing page has a share button and we'd like to include it into our journey. We should define an action to represent it.
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
Journeyviz.configure do |journey|
|
46
|
+
journey.screen :landing_page do |landing|
|
47
|
+
# Landing has an action called :share
|
48
|
+
landing.action :share
|
49
|
+
end
|
50
|
+
end
|
51
|
+
```
|
52
|
+
|
53
|
+
Imagine that the landing page also has an login form that sends the user to a logged-in area, into a dashboard page. Now we're going to use blocks and transitions.
|
54
|
+
|
55
|
+
```ruby
|
56
|
+
Journeyviz.configure do |journey|
|
57
|
+
journey.screen :landing_page do |landing|
|
58
|
+
landing.action :share
|
59
|
+
# Define an action `login` that transitions the user to dashboard page
|
60
|
+
# %i[logged dashboard] is the path to that screen. It includes every
|
61
|
+
# block and finally the screen name.
|
62
|
+
landing.action :login, transition: %i[logged dashboard]
|
63
|
+
end
|
64
|
+
|
65
|
+
# Defining the logged area
|
66
|
+
journey.block :logged do |logged_area|
|
67
|
+
# Dashboard inside logged area
|
68
|
+
logged_area.screen :dashboard
|
69
|
+
|
70
|
+
# Blocks can have blocks, you can call `logged_area.block :sublock` how many
|
71
|
+
# times you want.
|
72
|
+
end
|
73
|
+
end
|
74
|
+
```
|
75
|
+
|
76
|
+
### Visualizing journey
|
77
|
+
|
78
|
+
With journey definition loaded, you just have to mount the journeyviz server into your
|
79
|
+
rack application with `mount` command:
|
80
|
+
|
81
|
+
```ruby
|
82
|
+
mount Journeyviz::Server => '/journeyviz'
|
83
|
+
```
|
84
|
+
|
85
|
+
No just go to `/journeyviz` path on your application!
|
86
|
+
|
87
|
+
## Development
|
88
|
+
|
89
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
90
|
+
|
91
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
92
|
+
|
93
|
+
## Contributing
|
94
|
+
|
95
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/gabteles/journeyviz.
|
96
|
+
|
97
|
+
## License
|
98
|
+
|
99
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'journeyviz'
|
6
|
+
|
7
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
8
|
+
# with your gem easier. You can also use a different console, if you like.
|
9
|
+
|
10
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
11
|
+
# require "pry"
|
12
|
+
# Pry.start
|
13
|
+
|
14
|
+
require 'irb'
|
15
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/journeyviz.gemspec
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'journeyviz/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = 'journeyviz'
|
9
|
+
spec.version = Journeyviz::VERSION
|
10
|
+
spec.authors = ['Gabriel Teles']
|
11
|
+
spec.email = ['gabz.teles@gmail.com']
|
12
|
+
|
13
|
+
spec.summary = 'Journey visualization made easy'
|
14
|
+
spec.description = 'Define your user journey as code and track it historically and in realtime'
|
15
|
+
spec.homepage = 'https://github.com/gabteles/journeyviz'
|
16
|
+
spec.license = 'MIT'
|
17
|
+
|
18
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
19
|
+
spec.metadata['source_code_uri'] = spec.homepage
|
20
|
+
spec.metadata['changelog_uri'] = "#{spec.homepage}/CHANGELOG.md"
|
21
|
+
|
22
|
+
# Specify which files should be added to the gem when it is released.
|
23
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
24
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
25
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
26
|
+
end
|
27
|
+
spec.bindir = 'exe'
|
28
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
29
|
+
spec.require_paths = ['lib']
|
30
|
+
|
31
|
+
spec.add_development_dependency 'bundler', '~> 2.0'
|
32
|
+
spec.add_development_dependency 'byebug'
|
33
|
+
spec.add_development_dependency 'rake', '~> 13.0'
|
34
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
35
|
+
spec.add_development_dependency 'rubocop', '~> 0.81.0'
|
36
|
+
spec.add_development_dependency 'simplecov'
|
37
|
+
spec.add_dependency 'rack-app'
|
38
|
+
end
|
data/lib/journeyviz.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'journeyviz/version'
|
4
|
+
require 'journeyviz/server'
|
5
|
+
require 'journeyviz/journey'
|
6
|
+
|
7
|
+
module Journeyviz
|
8
|
+
class Error < StandardError; end
|
9
|
+
class InvalidNameError < Error; end
|
10
|
+
class DuplicatedDefinition < Error; end
|
11
|
+
class InvalidTransition < Error; end
|
12
|
+
|
13
|
+
class << self
|
14
|
+
def configure(&block)
|
15
|
+
@journey = Journey.new
|
16
|
+
block.call(journey)
|
17
|
+
journey.validate!
|
18
|
+
end
|
19
|
+
|
20
|
+
def identify(user_id)
|
21
|
+
context[:user_id] = user_id
|
22
|
+
end
|
23
|
+
|
24
|
+
def visit(screen)
|
25
|
+
# TODO
|
26
|
+
# screen/day/:day/visits ++
|
27
|
+
# screen/week/:week/visits ++
|
28
|
+
# screen/month/:month/visits ++
|
29
|
+
# screen/quarter/:quarter/visits ++
|
30
|
+
end
|
31
|
+
|
32
|
+
def act(action)
|
33
|
+
# TODO
|
34
|
+
# :action/day/:day ++
|
35
|
+
# :action/week/:week ++
|
36
|
+
# :action/month/:month ++
|
37
|
+
# :action/quarter/:quarter ++
|
38
|
+
end
|
39
|
+
|
40
|
+
def context
|
41
|
+
Thread.current[:journeyviz_context] ||= {}
|
42
|
+
end
|
43
|
+
|
44
|
+
def journey
|
45
|
+
@journey ||= Journey.new
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'journeyviz/normalized_name'
|
4
|
+
|
5
|
+
module Journeyviz
|
6
|
+
class Action
|
7
|
+
include NormalizedName
|
8
|
+
attr_reader :screen
|
9
|
+
|
10
|
+
def initialize(name, screen, transition: nil)
|
11
|
+
assign_normalize_name(name)
|
12
|
+
@screen = screen
|
13
|
+
@transition = transition
|
14
|
+
end
|
15
|
+
|
16
|
+
def transition
|
17
|
+
case @transition
|
18
|
+
when Symbol then find_screen_by_name(@transition)
|
19
|
+
when Array then find_screen_by_full_qualifier(@transition)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def raw_transition
|
24
|
+
@transition
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def find_screen_by_name(screen_name)
|
30
|
+
qualifier = screen.full_qualifier
|
31
|
+
|
32
|
+
(qualifier.size - 1).downto(0) do |len|
|
33
|
+
found_screen = find_screen_by_full_qualifier(qualifier[0, len] + [screen_name])
|
34
|
+
return found_screen if found_screen
|
35
|
+
end
|
36
|
+
|
37
|
+
nil
|
38
|
+
end
|
39
|
+
|
40
|
+
def find_screen_by_full_qualifier(qualifier)
|
41
|
+
screen.root_scope.find_screen(qualifier)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'journeyviz/normalized_name'
|
4
|
+
require 'journeyviz/node_group'
|
5
|
+
require 'journeyviz/graphable'
|
6
|
+
require 'journeyviz/scopable'
|
7
|
+
|
8
|
+
module Journeyviz
|
9
|
+
class Block
|
10
|
+
include NormalizedName
|
11
|
+
include NodeGroup
|
12
|
+
include Graphable
|
13
|
+
include Scopable[:name]
|
14
|
+
|
15
|
+
def initialize(name, scope = nil)
|
16
|
+
assign_normalize_name(name)
|
17
|
+
assign_scope(scope)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'journeyviz/graphable/externals'
|
4
|
+
require 'journeyviz/graphable/transitions'
|
5
|
+
|
6
|
+
module Journeyviz
|
7
|
+
module Graphable
|
8
|
+
include Externals
|
9
|
+
include Transitions
|
10
|
+
|
11
|
+
def graph
|
12
|
+
defs = [
|
13
|
+
*graph_screens,
|
14
|
+
*graph_blocks,
|
15
|
+
*graph_inputs,
|
16
|
+
*graph_outputs,
|
17
|
+
*graph_transitions,
|
18
|
+
*graph_styles
|
19
|
+
].join("\n")
|
20
|
+
|
21
|
+
"graph LR\n#{defs}"
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def graph_screens
|
27
|
+
(@screens || []).map { |screen| "#{graph_id(screen)}[#{screen.name}]:::screen" }
|
28
|
+
end
|
29
|
+
|
30
|
+
def graph_blocks
|
31
|
+
(@blocks || []).map { |block| "#{graph_id(block)}[#{block.name}]:::block" }
|
32
|
+
end
|
33
|
+
|
34
|
+
def graph_styles
|
35
|
+
[
|
36
|
+
'classDef screen fill:#373496,stroke:#373496,stroke-width:2px,color:#fff',
|
37
|
+
'classDef transition fill:#fff,stroke:#373496,stroke-width:2px,color:#373496,stroke-dasharray: 5, 5',
|
38
|
+
'classDef block fill:#963734,color:#fff'
|
39
|
+
]
|
40
|
+
end
|
41
|
+
|
42
|
+
def graph_id(node)
|
43
|
+
case node
|
44
|
+
when Journeyviz::Screen then screen_id(node)
|
45
|
+
when Journeyviz::Block then block_id(node)
|
46
|
+
when Journeyviz::Action then action_id(node)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def screen_id(node)
|
51
|
+
"screen_#{node.full_qualifier.join('_')}"
|
52
|
+
end
|
53
|
+
|
54
|
+
def block_id(node)
|
55
|
+
"block_#{node.full_qualifier.join('_')}"
|
56
|
+
end
|
57
|
+
|
58
|
+
def action_id(node)
|
59
|
+
from_id = graph_id(node.screen)
|
60
|
+
to_id = graph_target(node)
|
61
|
+
"transition_#{from_id}_#{node.name}_#{to_id}"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Journeyviz
|
4
|
+
module Graphable
|
5
|
+
module Externals
|
6
|
+
private
|
7
|
+
|
8
|
+
def graph_inputs
|
9
|
+
return '' unless defined?(root_scope)
|
10
|
+
|
11
|
+
inputs = self.inputs.map { |screen| screen.full_scope + [screen] }
|
12
|
+
|
13
|
+
return '' if inputs.empty?
|
14
|
+
|
15
|
+
<<~INPUTS
|
16
|
+
subgraph inputs
|
17
|
+
#{graph_external_chain(inputs, 'input')}
|
18
|
+
end
|
19
|
+
INPUTS
|
20
|
+
end
|
21
|
+
|
22
|
+
def graph_outputs
|
23
|
+
outputs = self.outputs.map { |screen| screen.full_scope + [screen] }
|
24
|
+
return '' if outputs.empty?
|
25
|
+
|
26
|
+
<<~OUTPUTS
|
27
|
+
subgraph outputs
|
28
|
+
#{graph_external_chain(outputs, 'output')}
|
29
|
+
end
|
30
|
+
OUTPUTS
|
31
|
+
end
|
32
|
+
|
33
|
+
def graph_external_chain(nodes, sufix)
|
34
|
+
nodes
|
35
|
+
.group_by(&:first)
|
36
|
+
.flat_map { |parent, chain| graph_external_chain_node(parent, chain, sufix) }
|
37
|
+
.join("\n")
|
38
|
+
end
|
39
|
+
|
40
|
+
def graph_external_chain_node(parent, chain, sufix)
|
41
|
+
chain_without_parent = chain.map { |subchain| subchain[1..-1] }
|
42
|
+
|
43
|
+
if parent.is_a?(Journeyviz::Screen)
|
44
|
+
[graph_external_screen(parent, sufix)] + graph_external_transitions(parent)
|
45
|
+
else
|
46
|
+
graph_external_block(parent, chain_without_parent, sufix)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def graph_external_screen(screen, sufix)
|
51
|
+
"#{sufix}_#{graph_id(screen)}[#{screen.name}]:::external_screen"
|
52
|
+
end
|
53
|
+
|
54
|
+
def graph_external_transitions(screen)
|
55
|
+
screen
|
56
|
+
.actions
|
57
|
+
.select { |action| screens.include?(action.transition) }
|
58
|
+
.map { |action| "#{graph_id(action)}(#{action.name}):::transition" }
|
59
|
+
end
|
60
|
+
|
61
|
+
def graph_external_block(block, chain, sufix)
|
62
|
+
definition = graph_external_chain(chain, sufix)
|
63
|
+
return definition if block.is_a?(Journeyviz::Journey)
|
64
|
+
|
65
|
+
<<~SUBGRAPH
|
66
|
+
subgraph #{sufix}_#{graph_id(block)}[#{block.name}]
|
67
|
+
#{definition}
|
68
|
+
end
|
69
|
+
SUBGRAPH
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Journeyviz
|
4
|
+
module Graphable
|
5
|
+
module Transitions
|
6
|
+
private
|
7
|
+
|
8
|
+
def graph_transitions
|
9
|
+
[
|
10
|
+
*graph_screen_transitions,
|
11
|
+
*graph_block_transitions,
|
12
|
+
*graph_external_inputs
|
13
|
+
]
|
14
|
+
end
|
15
|
+
|
16
|
+
def graph_transition_node(action)
|
17
|
+
from_id = graph_id(action.screen)
|
18
|
+
to_id = graph_target(action)
|
19
|
+
"transition_#{from_id}_#{action.name}_#{to_id}(#{action.name}):::transition"
|
20
|
+
end
|
21
|
+
|
22
|
+
def graph_screen_transitions
|
23
|
+
@screens
|
24
|
+
.flat_map(&:actions)
|
25
|
+
.select(&:transition)
|
26
|
+
.flat_map do |action|
|
27
|
+
[graph_transition_node(action), graph_screen_transition(action)]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def graph_screen_transition(action)
|
32
|
+
"#{graph_id(action.screen)} --- #{graph_id(action)} --> #{graph_target(action)}"
|
33
|
+
end
|
34
|
+
|
35
|
+
def graph_target(action)
|
36
|
+
target = action.transition
|
37
|
+
|
38
|
+
return "output_#{graph_id(target)}" if outputs.include?(target)
|
39
|
+
|
40
|
+
direct_children = (@blocks || []) + (@screens || [])
|
41
|
+
target = target.scope until direct_children.include?(target)
|
42
|
+
graph_id(target)
|
43
|
+
end
|
44
|
+
|
45
|
+
def graph_external_inputs
|
46
|
+
inputs
|
47
|
+
.flat_map(&:actions)
|
48
|
+
.select { |action| screens.include?(action.transition) }
|
49
|
+
.map { |action| "input_#{graph_screen_transition(action)}" }
|
50
|
+
end
|
51
|
+
|
52
|
+
def graph_block_transitions
|
53
|
+
@blocks.flat_map do |block|
|
54
|
+
targets = block.outputs
|
55
|
+
screen_targets = @screens & targets
|
56
|
+
block_targets = @blocks.select do |target_block|
|
57
|
+
targets.any? { |target| target_block.screens.include?(target) }
|
58
|
+
end
|
59
|
+
|
60
|
+
(screen_targets + block_targets).map do |target|
|
61
|
+
"#{graph_id(block)} --> #{graph_id(target)}"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'journeyviz/node_group'
|
4
|
+
require 'journeyviz/graphable'
|
5
|
+
require 'journeyviz/block'
|
6
|
+
|
7
|
+
module Journeyviz
|
8
|
+
class Journey
|
9
|
+
include NodeGroup
|
10
|
+
include Graphable
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@blocks = []
|
14
|
+
end
|
15
|
+
|
16
|
+
def validate!
|
17
|
+
screens.each do |screen|
|
18
|
+
screen.actions.each { |action| validate_action(action) }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def validate_action(action)
|
25
|
+
return if action.transition || !action.raw_transition
|
26
|
+
|
27
|
+
message = "Action #{action.name.inspect} "
|
28
|
+
message += "on screen #{action.screen.full_qualifier.inspect} "
|
29
|
+
message += "has invalid transition: #{action.raw_transition.inspect}"
|
30
|
+
raise(Journeyviz::InvalidTransition, message)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'journeyviz/screen'
|
4
|
+
|
5
|
+
module Journeyviz
|
6
|
+
autoload :Block, 'journeyviz/block'
|
7
|
+
|
8
|
+
module NodeGroup
|
9
|
+
def block(name, &definition)
|
10
|
+
block = Block.new(name, self)
|
11
|
+
|
12
|
+
@blocks ||= []
|
13
|
+
if @blocks.any? { |defined_block| block.name == defined_block.name }
|
14
|
+
raise DuplicatedDefinition, "Duplicated block name: #{name}"
|
15
|
+
end
|
16
|
+
|
17
|
+
@blocks.push(block)
|
18
|
+
definition.call(block)
|
19
|
+
block
|
20
|
+
end
|
21
|
+
|
22
|
+
def blocks(include_children: false)
|
23
|
+
@blocks ||= []
|
24
|
+
|
25
|
+
if include_children
|
26
|
+
@blocks.flat_map { |block| [block] + block.blocks(include_children: true) }
|
27
|
+
else
|
28
|
+
@blocks
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def screen(name)
|
33
|
+
@screens ||= []
|
34
|
+
screen = Screen.new(name, self)
|
35
|
+
|
36
|
+
if @screens.any? { |defined_screen| screen.name == defined_screen.name }
|
37
|
+
raise DuplicatedDefinition, "Duplicated screen name: #{name}"
|
38
|
+
end
|
39
|
+
|
40
|
+
@screens.push(screen)
|
41
|
+
yield screen if block_given?
|
42
|
+
screen
|
43
|
+
end
|
44
|
+
|
45
|
+
def screens
|
46
|
+
@screens ||= []
|
47
|
+
@screens + blocks.flat_map(&:screens)
|
48
|
+
end
|
49
|
+
|
50
|
+
def find_screen(qualifier)
|
51
|
+
screens.find { |screen| screen.full_qualifier == qualifier }
|
52
|
+
end
|
53
|
+
|
54
|
+
def inputs
|
55
|
+
options = defined?(root_scope) && root_scope ? root_scope.screens : []
|
56
|
+
external_screens = options - screens
|
57
|
+
self_screens = screens
|
58
|
+
external_screens.select do |screen|
|
59
|
+
screen.actions.any? do |action|
|
60
|
+
self_screens.include?(action.transition)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def outputs
|
66
|
+
screens
|
67
|
+
.flat_map(&:actions)
|
68
|
+
.map(&:transition)
|
69
|
+
.compact
|
70
|
+
.reject { |screen| screens.include?(screen) }
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Journeyviz
|
4
|
+
module NormalizedName
|
5
|
+
attr_reader :name
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def assign_normalize_name(name)
|
10
|
+
if !name.is_a?(String) && !name.is_a?(Symbol) || name.size <= 0
|
11
|
+
raise InvalidNameError, "Invalid name given: #{name.inspect}"
|
12
|
+
end
|
13
|
+
|
14
|
+
@name = name.to_sym
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Journeyviz
|
4
|
+
Scopable = proc do |qualified_by|
|
5
|
+
Module.new do
|
6
|
+
attr_reader :scope
|
7
|
+
|
8
|
+
def assign_scope(scope)
|
9
|
+
@scope = scope
|
10
|
+
end
|
11
|
+
|
12
|
+
def root_scope
|
13
|
+
current_scope = self
|
14
|
+
current_scope = current_scope.scope until !current_scope.respond_to?(:scope) || current_scope.scope.nil?
|
15
|
+
current_scope
|
16
|
+
end
|
17
|
+
|
18
|
+
def full_scope
|
19
|
+
current_scope = scope
|
20
|
+
chain = [current_scope]
|
21
|
+
|
22
|
+
until current_scope.nil? || !current_scope.respond_to?(:scope)
|
23
|
+
current_scope = current_scope.scope
|
24
|
+
chain.unshift(current_scope)
|
25
|
+
end
|
26
|
+
|
27
|
+
chain
|
28
|
+
end
|
29
|
+
|
30
|
+
define_method(:full_qualifier) do
|
31
|
+
base_qualifier = scope&.respond_to?(:full_qualifier) ? scope.full_qualifier : []
|
32
|
+
[*base_qualifier, send(qualified_by)].compact
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'journeyviz/action'
|
4
|
+
require 'journeyviz/normalized_name'
|
5
|
+
require 'journeyviz/scopable'
|
6
|
+
|
7
|
+
module Journeyviz
|
8
|
+
class Screen
|
9
|
+
include NormalizedName
|
10
|
+
include Scopable[:name]
|
11
|
+
attr_reader :actions, :scope
|
12
|
+
|
13
|
+
def initialize(name, scope = nil)
|
14
|
+
assign_normalize_name(name)
|
15
|
+
@actions = []
|
16
|
+
assign_scope(scope)
|
17
|
+
end
|
18
|
+
|
19
|
+
def action(name, **params)
|
20
|
+
action = Action.new(name, self, **params)
|
21
|
+
|
22
|
+
if actions.any? { |defined_action| action.name == defined_action.name }
|
23
|
+
raise DuplicatedDefinition, "Duplicated action name: #{name}"
|
24
|
+
end
|
25
|
+
|
26
|
+
@actions << action
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rack/app'
|
4
|
+
|
5
|
+
module Journeyviz
|
6
|
+
class Server < Rack::App
|
7
|
+
get '/' do
|
8
|
+
block_path = params['block'] || ''
|
9
|
+
@block = block_path.split('_').reduce(Journeyviz.journey) do |current, step|
|
10
|
+
current.blocks.find { |block| block.name == step.to_sym }
|
11
|
+
end
|
12
|
+
|
13
|
+
path = File.expand_path('server/index.html.erb', __dir__)
|
14
|
+
ERB.new(File.read(path)).result(binding)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<meta charset="utf-8">
|
5
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
|
7
|
+
<title>Journeyviz</title>
|
8
|
+
<link rel="icon" type="image/png" href="data:image/png;base64,iVBORw0KGgo=">
|
9
|
+
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
|
10
|
+
<link rel="stylesheet" href="https://code.getmdl.io/1.3.0/material.indigo-pink.min.css">
|
11
|
+
<style>
|
12
|
+
.mermaid {
|
13
|
+
margin: 2em auto;
|
14
|
+
display: none;
|
15
|
+
max-width: 100%;
|
16
|
+
overflow: auto;
|
17
|
+
border-radius: 5px;
|
18
|
+
background: #ddd;
|
19
|
+
}
|
20
|
+
|
21
|
+
.mermaid[data-processed] {
|
22
|
+
display: block;
|
23
|
+
}
|
24
|
+
|
25
|
+
.demo-drawer {
|
26
|
+
border: none;
|
27
|
+
}
|
28
|
+
/* iOS Safari specific workaround */
|
29
|
+
.demo-drawer .mdl-menu__container {
|
30
|
+
z-index: -1;
|
31
|
+
}
|
32
|
+
.demo-drawer .demo-navigation {
|
33
|
+
z-index: -2;
|
34
|
+
}
|
35
|
+
.demo-navigation {
|
36
|
+
flex-grow: 1;
|
37
|
+
}
|
38
|
+
.demo-layout .mdl-layout__header .mdl-layout__drawer-button {
|
39
|
+
color: rgba(0, 0, 0, 0.54);
|
40
|
+
}
|
41
|
+
.demo-layout .demo-navigation .mdl-navigation__link {
|
42
|
+
display: flex !important;
|
43
|
+
flex-direction: row;
|
44
|
+
align-items: center;
|
45
|
+
color: rgba(255, 255, 255, 0.56);
|
46
|
+
font-weight: 500;
|
47
|
+
}
|
48
|
+
.demo-layout .demo-navigation .mdl-navigation__link:hover {
|
49
|
+
background-color: #00BCD4;
|
50
|
+
color: #37474F;
|
51
|
+
}
|
52
|
+
.demo-navigation hr {
|
53
|
+
border-color: rgba(0,0,0,0.34);
|
54
|
+
}
|
55
|
+
.demo-navigation .mdl-navigation__link .material-icons {
|
56
|
+
font-size: 24px;
|
57
|
+
color: rgba(255, 255, 255, 0.56);
|
58
|
+
margin-right: 32px;
|
59
|
+
}
|
60
|
+
</style>
|
61
|
+
</head>
|
62
|
+
|
63
|
+
<body>
|
64
|
+
<div class="demo-layout mdl-layout mdl-js-layout mdl-layout--fixed-drawer mdl-layout--fixed-header">
|
65
|
+
<header class="demo-header mdl-layout__header mdl-color--grey-100 mdl-color-text--grey-600">
|
66
|
+
<div class="mdl-layout__header-row">
|
67
|
+
<span class="mdl-layout-title">
|
68
|
+
<%= @block.respond_to?(:name) ? "#{@block.name.to_s.split('_').collect(&:capitalize).join(' ')} / Journeyviz" : 'Journeyviz' %>
|
69
|
+
</span>
|
70
|
+
</div>
|
71
|
+
</header>
|
72
|
+
|
73
|
+
<div class="demo-drawer mdl-layout__drawer mdl-color--blue-grey-900 mdl-color-text--blue-grey-50">
|
74
|
+
<nav class="demo-navigation mdl-navigation mdl-color--blue-grey-800">
|
75
|
+
<a class="mdl-navigation__link" href="?">
|
76
|
+
<i class="mdl-color-text--blue-grey-400 material-icons" role="presentation">home</i>
|
77
|
+
<span>Journey</span>
|
78
|
+
</a>
|
79
|
+
<hr/>
|
80
|
+
<% @block.blocks.each do |block| %>
|
81
|
+
<a class="mdl-navigation__link" href="?block=<%= block.full_qualifier.join('_') %>">
|
82
|
+
<i class="mdl-color-text--blue-grey-400 material-icons" role="presentation">layers</i>
|
83
|
+
<span><%= block.name.to_s.split('_').collect(&:capitalize).join(' ') %></span>
|
84
|
+
</a>
|
85
|
+
<% end %>
|
86
|
+
</nav>
|
87
|
+
</div>
|
88
|
+
|
89
|
+
<main class="mdl-layout__content mdl-color--grey-100">
|
90
|
+
<div class="mdl-grid demo-content">
|
91
|
+
<div class="mermaid"><%= @block.graph %></div>
|
92
|
+
</div>
|
93
|
+
</main>
|
94
|
+
</div>
|
95
|
+
|
96
|
+
<script defer src="https://code.getmdl.io/1.3.0/material.min.js"></script>
|
97
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/mermaid/8.4.8/mermaid.min.js"></script>
|
98
|
+
<script>mermaid.initialize({ startOnLoad: true });</script>
|
99
|
+
</body>
|
100
|
+
</html>
|
metadata
ADDED
@@ -0,0 +1,172 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: journeyviz
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Gabriel Teles
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-06-30 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: byebug
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '13.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '13.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rubocop
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 0.81.0
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 0.81.0
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: simplecov
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rack-app
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
description: Define your user journey as code and track it historically and in realtime
|
112
|
+
email:
|
113
|
+
- gabz.teles@gmail.com
|
114
|
+
executables: []
|
115
|
+
extensions: []
|
116
|
+
extra_rdoc_files: []
|
117
|
+
files:
|
118
|
+
- ".github/workflows/gem-push.yml"
|
119
|
+
- ".github/workflows/tests.yml"
|
120
|
+
- ".gitignore"
|
121
|
+
- ".rspec"
|
122
|
+
- ".rubocop.yml"
|
123
|
+
- ".travis.yml"
|
124
|
+
- Gemfile
|
125
|
+
- Gemfile.lock
|
126
|
+
- LICENSE.txt
|
127
|
+
- README.md
|
128
|
+
- Rakefile
|
129
|
+
- bin/console
|
130
|
+
- bin/setup
|
131
|
+
- journeyviz.gemspec
|
132
|
+
- lib/journeyviz.rb
|
133
|
+
- lib/journeyviz/action.rb
|
134
|
+
- lib/journeyviz/block.rb
|
135
|
+
- lib/journeyviz/graphable.rb
|
136
|
+
- lib/journeyviz/graphable/externals.rb
|
137
|
+
- lib/journeyviz/graphable/transitions.rb
|
138
|
+
- lib/journeyviz/journey.rb
|
139
|
+
- lib/journeyviz/node_group.rb
|
140
|
+
- lib/journeyviz/normalized_name.rb
|
141
|
+
- lib/journeyviz/scopable.rb
|
142
|
+
- lib/journeyviz/screen.rb
|
143
|
+
- lib/journeyviz/server.rb
|
144
|
+
- lib/journeyviz/server/index.html.erb
|
145
|
+
- lib/journeyviz/version.rb
|
146
|
+
homepage: https://github.com/gabteles/journeyviz
|
147
|
+
licenses:
|
148
|
+
- MIT
|
149
|
+
metadata:
|
150
|
+
homepage_uri: https://github.com/gabteles/journeyviz
|
151
|
+
source_code_uri: https://github.com/gabteles/journeyviz
|
152
|
+
changelog_uri: https://github.com/gabteles/journeyviz/CHANGELOG.md
|
153
|
+
post_install_message:
|
154
|
+
rdoc_options: []
|
155
|
+
require_paths:
|
156
|
+
- lib
|
157
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
158
|
+
requirements:
|
159
|
+
- - ">="
|
160
|
+
- !ruby/object:Gem::Version
|
161
|
+
version: '0'
|
162
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
|
+
requirements: []
|
168
|
+
rubygems_version: 3.0.3
|
169
|
+
signing_key:
|
170
|
+
specification_version: 4
|
171
|
+
summary: Journey visualization made easy
|
172
|
+
test_files: []
|