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