guide 0.0.1 → 0.5.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 +5 -5
- data/LICENSE +22 -0
- data/Rakefile +18 -0
- data/app/assets/javascripts/guide/application.js +21723 -0
- data/app/assets/javascripts/guide/scenario.js +12642 -0
- data/app/assets/stylesheets/guide/application.css +141 -0
- data/app/controllers/guide/base_controller.rb +83 -0
- data/app/controllers/guide/nodes_controller.rb +40 -0
- data/app/controllers/guide/scenarios_controller.rb +44 -0
- data/app/helpers/guide/application_helper.rb +4 -0
- data/app/helpers/guide/document_helper.rb +20 -0
- data/app/models/guide/arborist.rb +44 -0
- data/app/models/guide/bouncer.rb +34 -0
- data/app/models/guide/cartographer.rb +49 -0
- data/app/models/guide/content.rb +28 -0
- data/app/models/guide/default_authentication_system.rb +13 -0
- data/app/models/guide/default_authorisation_system.rb +18 -0
- data/app/models/guide/diplomat.rb +60 -0
- data/app/models/guide/document.rb +20 -0
- data/app/models/guide/endpoint_stocktaker.rb +94 -0
- data/app/models/guide/errors.rb +2 -0
- data/app/models/guide/errors/base.rb +6 -0
- data/app/models/guide/errors/interface_violation.rb +2 -0
- data/app/models/guide/errors/invalid_node.rb +2 -0
- data/app/models/guide/errors/invalid_scenario.rb +2 -0
- data/app/models/guide/errors/invalid_visibility_option.rb +2 -0
- data/app/models/guide/errors/permission_denied.rb +2 -0
- data/app/models/guide/fixture.rb +15 -0
- data/app/models/guide/fixtures.rb +3 -0
- data/app/models/guide/form_object.rb +3 -0
- data/app/models/guide/monkey.rb +47 -0
- data/app/models/guide/nobilizer.rb +9 -0
- data/app/models/guide/node.rb +67 -0
- data/app/models/guide/photographer.rb +20 -0
- data/app/models/guide/scout.rb +59 -0
- data/app/models/guide/simulator.rb +26 -0
- data/app/models/guide/structure.rb +75 -0
- data/app/models/guide/view_model.rb +37 -0
- data/app/view_models/guide/layout_view.rb +81 -0
- data/app/view_models/guide/navigation_view.rb +25 -0
- data/app/view_models/guide/node_view.rb +59 -0
- data/app/view_models/guide/scenario_layout_view.rb +49 -0
- data/app/view_models/guide/scenario_view.rb +39 -0
- data/app/views/guide/_content.html.erb +17 -0
- data/app/views/guide/common/_category.html.erb +9 -0
- data/app/views/guide/common/_footer.html.erb +21 -0
- data/app/views/guide/common/_locale_switcher.html.erb +6 -0
- data/app/views/guide/common/_navigation.html.erb +12 -0
- data/app/views/guide/common/_navigation_node.html.erb +39 -0
- data/app/views/guide/common/_page_title.html.erb +8 -0
- data/app/views/guide/common/_search.html.erb +23 -0
- data/app/views/guide/common/_unsupported_browser_message.html.erb +6 -0
- data/app/views/guide/common/_visibility_banner.html.erb +25 -0
- data/app/views/guide/nodes/_document.html.erb +3 -0
- data/app/views/guide/nodes/_scenario.html.erb +14 -0
- data/app/views/guide/nodes/_scenario_list.html.erb +10 -0
- data/app/views/guide/nodes/_structure.html.erb +26 -0
- data/app/views/guide/nodes/_template_location.html.erb +7 -0
- data/app/views/guide/nodes/show.html.erb +7 -0
- data/app/views/guide/scenarios/_scenario.html.erb +25 -0
- data/app/views/guide/scenarios/scenario/_locale_switcher.html.erb +5 -0
- data/app/views/guide/scenarios/scenario/_toolbar.html.erb +39 -0
- data/app/views/guide/scenarios/scenario/_visibility.html.erb +10 -0
- data/app/views/guide/scenarios/show.html.erb +1 -0
- data/app/views/layouts/guide/application.html.erb +76 -0
- data/app/views/layouts/guide/scenario.html.erb +38 -0
- data/app/views/layouts/guide/scenario/default.html.erb +1 -0
- data/app/views/layouts/guide/scenario/default.text.erb +1 -0
- data/config/initializers/assets.rb +1 -0
- data/config/locales/models/guide/monkey.en.yml +6 -0
- data/config/locales/views/guide/nodes/_structure.en.yml +7 -0
- data/config/routes.rb +6 -0
- data/lib/guide.rb +12 -0
- data/lib/guide/authorisation_spec_helper.rb +83 -0
- data/lib/guide/configuration.rb +29 -0
- data/lib/guide/consistency_spec_helper.rb +33 -0
- data/lib/guide/engine.rb +9 -0
- data/lib/guide/version.rb +3 -0
- data/lib/tasks/guide_tasks.rake +4 -0
- metadata +207 -16
- data/.gitignore +0 -26
- data/Gemfile +0 -4
- data/guide.gemspec +0 -12
@@ -0,0 +1,141 @@
|
|
1
|
+
/**
|
2
|
+
* prism.js default theme for JavaScript, CSS and HTML
|
3
|
+
* Based on dabblet (http://dabblet.com)
|
4
|
+
* @author Lea Verou
|
5
|
+
*/
|
6
|
+
|
7
|
+
code[class*="language-"],
|
8
|
+
pre[class*="language-"] {
|
9
|
+
color: black;
|
10
|
+
background: none;
|
11
|
+
text-shadow: 0 1px white;
|
12
|
+
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
|
13
|
+
direction: ltr;
|
14
|
+
text-align: left;
|
15
|
+
white-space: pre;
|
16
|
+
word-spacing: normal;
|
17
|
+
word-break: normal;
|
18
|
+
word-wrap: normal;
|
19
|
+
line-height: 1.5;
|
20
|
+
|
21
|
+
-moz-tab-size: 4;
|
22
|
+
-o-tab-size: 4;
|
23
|
+
tab-size: 4;
|
24
|
+
|
25
|
+
-webkit-hyphens: none;
|
26
|
+
-moz-hyphens: none;
|
27
|
+
-ms-hyphens: none;
|
28
|
+
hyphens: none;
|
29
|
+
}
|
30
|
+
|
31
|
+
pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection,
|
32
|
+
code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection {
|
33
|
+
text-shadow: none;
|
34
|
+
background: #b3d4fc;
|
35
|
+
}
|
36
|
+
|
37
|
+
pre[class*="language-"]::selection, pre[class*="language-"] ::selection,
|
38
|
+
code[class*="language-"]::selection, code[class*="language-"] ::selection {
|
39
|
+
text-shadow: none;
|
40
|
+
background: #b3d4fc;
|
41
|
+
}
|
42
|
+
|
43
|
+
@media print {
|
44
|
+
code[class*="language-"],
|
45
|
+
pre[class*="language-"] {
|
46
|
+
text-shadow: none;
|
47
|
+
}
|
48
|
+
}
|
49
|
+
|
50
|
+
/* Code blocks */
|
51
|
+
|
52
|
+
pre[class*="language-"] {
|
53
|
+
padding: 1em;
|
54
|
+
margin: .5em 0;
|
55
|
+
overflow: auto;
|
56
|
+
}
|
57
|
+
|
58
|
+
:not(pre) > code[class*="language-"],
|
59
|
+
pre[class*="language-"] {
|
60
|
+
background: #f5f2f0;
|
61
|
+
}
|
62
|
+
|
63
|
+
/* Inline code */
|
64
|
+
|
65
|
+
:not(pre) > code[class*="language-"] {
|
66
|
+
padding: .1em;
|
67
|
+
border-radius: .3em;
|
68
|
+
white-space: normal;
|
69
|
+
}
|
70
|
+
|
71
|
+
.token.comment,
|
72
|
+
.token.prolog,
|
73
|
+
.token.doctype,
|
74
|
+
.token.cdata {
|
75
|
+
color: slategray;
|
76
|
+
}
|
77
|
+
|
78
|
+
.token.punctuation {
|
79
|
+
color: #999;
|
80
|
+
}
|
81
|
+
|
82
|
+
.namespace {
|
83
|
+
opacity: .7;
|
84
|
+
}
|
85
|
+
|
86
|
+
.token.property,
|
87
|
+
.token.tag,
|
88
|
+
.token.boolean,
|
89
|
+
.token.number,
|
90
|
+
.token.constant,
|
91
|
+
.token.symbol,
|
92
|
+
.token.deleted {
|
93
|
+
color: #905;
|
94
|
+
}
|
95
|
+
|
96
|
+
.token.selector,
|
97
|
+
.token.attr-name,
|
98
|
+
.token.string,
|
99
|
+
.token.char,
|
100
|
+
.token.builtin,
|
101
|
+
.token.inserted {
|
102
|
+
color: #690;
|
103
|
+
}
|
104
|
+
|
105
|
+
.token.operator,
|
106
|
+
.token.entity,
|
107
|
+
.token.url,
|
108
|
+
.language-css .token.string,
|
109
|
+
.style .token.string {
|
110
|
+
color: #a67f59;
|
111
|
+
background: hsla(0, 0%, 100%, .5);
|
112
|
+
}
|
113
|
+
|
114
|
+
.token.atrule,
|
115
|
+
.token.attr-value,
|
116
|
+
.token.keyword {
|
117
|
+
color: #07a;
|
118
|
+
}
|
119
|
+
|
120
|
+
.token.function {
|
121
|
+
color: #DD4A68;
|
122
|
+
}
|
123
|
+
|
124
|
+
.token.regex,
|
125
|
+
.token.important,
|
126
|
+
.token.variable {
|
127
|
+
color: #e90;
|
128
|
+
}
|
129
|
+
|
130
|
+
.token.important,
|
131
|
+
.token.bold {
|
132
|
+
font-weight: bold;
|
133
|
+
}
|
134
|
+
|
135
|
+
.token.italic {
|
136
|
+
font-style: italic;
|
137
|
+
}
|
138
|
+
|
139
|
+
.token.entity {
|
140
|
+
cursor: help;
|
141
|
+
}
|
@@ -0,0 +1,83 @@
|
|
1
|
+
class Guide::BaseController < Guide.configuration.controller_class_to_inherit.constantize
|
2
|
+
helper Rails.application.routes.url_helpers
|
3
|
+
layout 'guide/application'
|
4
|
+
|
5
|
+
private
|
6
|
+
|
7
|
+
around_action :set_locale
|
8
|
+
def set_locale
|
9
|
+
I18n.with_locale(diplomat.negotiate_locale) { yield }
|
10
|
+
end
|
11
|
+
|
12
|
+
around_action :handle_known_errors
|
13
|
+
def handle_known_errors
|
14
|
+
begin
|
15
|
+
yield
|
16
|
+
rescue Guide::Errors::Base => error
|
17
|
+
raise error if Rails.env.development?
|
18
|
+
|
19
|
+
case error
|
20
|
+
when Guide::Errors::InvalidNode, Guide::Errors::PermissionDenied
|
21
|
+
render :plain => 'Nothing to see here.', :status => '404'
|
22
|
+
else
|
23
|
+
render :plain => "Something's gone wrong. Sorry about that.", :status => '500'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def active_node
|
29
|
+
@node ||= monkey.fetch_node(node_path)
|
30
|
+
end
|
31
|
+
|
32
|
+
def bouncer
|
33
|
+
@bouncer ||= Guide::Bouncer.new(authorisation_system: injected_authorisation_system)
|
34
|
+
end
|
35
|
+
|
36
|
+
def injected_authentication_system
|
37
|
+
if defined?(authentication_system)
|
38
|
+
authentication_system
|
39
|
+
else
|
40
|
+
Guide::DefaultAuthenticationSystem.new
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def injected_authorisation_system
|
45
|
+
if defined?(authorisation_system)
|
46
|
+
authorisation_system
|
47
|
+
else
|
48
|
+
Guide::DefaultAuthorisationSystem.new
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def injected_html
|
53
|
+
if defined?(html_injection)
|
54
|
+
html_injection
|
55
|
+
else
|
56
|
+
''
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def content
|
61
|
+
@content ||= Guide::Content.new
|
62
|
+
end
|
63
|
+
|
64
|
+
def diplomat
|
65
|
+
@diplomat ||= Guide::Diplomat.new(session, params, I18n.default_locale)
|
66
|
+
end
|
67
|
+
|
68
|
+
def monkey
|
69
|
+
Guide::Monkey.new(content, bouncer)
|
70
|
+
end
|
71
|
+
|
72
|
+
def nobilizer
|
73
|
+
Guide::Nobilizer.new
|
74
|
+
end
|
75
|
+
|
76
|
+
def active_node_visibility
|
77
|
+
Guide::Scout.new(content).visibility_along_path(node_path)
|
78
|
+
end
|
79
|
+
|
80
|
+
def node_path
|
81
|
+
params[:node_path] || ""
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
class Guide::NodesController < Guide::BaseController
|
2
|
+
def show
|
3
|
+
expose_layout
|
4
|
+
expose_navigation
|
5
|
+
expose_node
|
6
|
+
end
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def expose_layout
|
11
|
+
@layout_view = Guide::LayoutView.new(
|
12
|
+
bouncer: bouncer,
|
13
|
+
diplomat: diplomat,
|
14
|
+
content_node: content,
|
15
|
+
active_node: active_node,
|
16
|
+
active_node_heritage: nobilizer.bestow_heritage(node_path),
|
17
|
+
active_node_visibility: active_node_visibility,
|
18
|
+
active_node_title: nobilizer.bestow_title(node_path),
|
19
|
+
authentication_system: injected_authentication_system,
|
20
|
+
injected_html: injected_html,
|
21
|
+
)
|
22
|
+
end
|
23
|
+
|
24
|
+
def expose_navigation
|
25
|
+
@navigation_view = Guide::NavigationView.new(
|
26
|
+
bouncer: bouncer,
|
27
|
+
node: content,
|
28
|
+
active_node: active_node,
|
29
|
+
)
|
30
|
+
end
|
31
|
+
|
32
|
+
def expose_node
|
33
|
+
@node_view = Guide::NodeView.new(
|
34
|
+
node: active_node,
|
35
|
+
bouncer: bouncer,
|
36
|
+
diplomat: diplomat,
|
37
|
+
node_path: node_path,
|
38
|
+
)
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
class Guide::ScenariosController < Guide::BaseController
|
2
|
+
layout 'guide/scenario'
|
3
|
+
|
4
|
+
def show
|
5
|
+
expose_layout
|
6
|
+
expose_scenario
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def expose_layout
|
12
|
+
@layout_view = Guide::ScenarioLayoutView.new(
|
13
|
+
node: active_node,
|
14
|
+
node_title: nobilizer.bestow_title(node_path),
|
15
|
+
scenario: scenario,
|
16
|
+
format: scenario_format,
|
17
|
+
injected_html: injected_html,
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
def expose_scenario
|
22
|
+
@scenario_view = Guide::ScenarioView.new(
|
23
|
+
node: active_node,
|
24
|
+
scenario: scenario,
|
25
|
+
format: scenario_format,
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
def scenario
|
30
|
+
@scenario ||= simulator.fetch_scenario(scenario_id)
|
31
|
+
end
|
32
|
+
|
33
|
+
def simulator
|
34
|
+
@simulator ||= Guide::Simulator.new(active_node, bouncer)
|
35
|
+
end
|
36
|
+
|
37
|
+
def scenario_id
|
38
|
+
params[:scenario_id].to_sym
|
39
|
+
end
|
40
|
+
|
41
|
+
def scenario_format
|
42
|
+
params[:scenario_format]
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Guide::DocumentHelper
|
2
|
+
def code_example(language='markup', show_rendered_element=true, &block)
|
3
|
+
raise ArgumentError, "Missing block" unless block_given?
|
4
|
+
|
5
|
+
code_stream = capture(&block)
|
6
|
+
|
7
|
+
html = Nokogiri::HTML(code_stream.to_str)
|
8
|
+
code_string = html.css("span.example").collect(&:inner_html).join
|
9
|
+
code_string = code_stream.to_str if code_string.blank?
|
10
|
+
|
11
|
+
code_string = code_string.strip_heredoc.strip
|
12
|
+
|
13
|
+
capture do
|
14
|
+
concat content_tag(:div, code_stream, :class => "sg-content__demo") if show_rendered_element
|
15
|
+
concat content_tag(:pre, content_tag(:code, code_string),
|
16
|
+
:class => "sg-content__code language-#{language} line-numbers",
|
17
|
+
:"data-language" => language)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
class Guide::Arborist
|
2
|
+
def initialize(bouncer)
|
3
|
+
@bouncer = bouncer
|
4
|
+
end
|
5
|
+
|
6
|
+
def draw_paths_to_visible_leaf_nodes(starting_node:, include_first_node: false)
|
7
|
+
initial_node_path = include_first_node ? starting_node.id.to_s : nil
|
8
|
+
|
9
|
+
recursively_cartograph_leaf_nodes(starting_node, initial_node_path, {})
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def recursively_cartograph_leaf_nodes(node, node_path, result)
|
15
|
+
node.child_nodes.each do |child_node_id, child_node|
|
16
|
+
child_node_path = iterate_on_node_path(node_path, child_node_id)
|
17
|
+
|
18
|
+
if @bouncer.user_can_access?(child_node)
|
19
|
+
if child_node.leaf_node?
|
20
|
+
result[child_node_path] = nobilizer.bestow_title(child_node_path)
|
21
|
+
else
|
22
|
+
recursively_cartograph_leaf_nodes(child_node,
|
23
|
+
child_node_path,
|
24
|
+
result)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
result
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def iterate_on_node_path(node_path, child_node_id)
|
34
|
+
if node_path
|
35
|
+
"#{node_path}/#{child_node_id}"
|
36
|
+
else
|
37
|
+
child_node_id.to_s
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def nobilizer
|
42
|
+
Guide::Nobilizer.new
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
class Guide::Bouncer
|
2
|
+
def initialize(authorisation_system:)
|
3
|
+
@authorisation_system = authorisation_system
|
4
|
+
end
|
5
|
+
|
6
|
+
def user_can_access?(node)
|
7
|
+
visible_to_user?(node.id, node.options[:visibility])
|
8
|
+
end
|
9
|
+
|
10
|
+
def user_is_privileged?
|
11
|
+
@authorisation_system.user_is_privileged?
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def visible_to_user?(label, visibility)
|
17
|
+
return true if visibility.blank?
|
18
|
+
|
19
|
+
if valid_visibility_options.include?(visibility)
|
20
|
+
@authorisation_system.allow?(:"view_guide_#{visibility}")
|
21
|
+
else
|
22
|
+
raise Guide::Errors::InvalidVisibilityOption, <<-EOS.gsub(' ,', ' nil,').squish
|
23
|
+
You tried to give :#{label} a visibility of :#{visibility},
|
24
|
+
but :#{visibility} is not a valid selection.
|
25
|
+
Valid visibility options include:
|
26
|
+
#{valid_visibility_options.join(', :')}.
|
27
|
+
EOS
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def valid_visibility_options
|
32
|
+
@authorisation_system.valid_visibility_options
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
class Guide::Cartographer
|
2
|
+
def initialize(bouncer)
|
3
|
+
@bouncer = bouncer
|
4
|
+
end
|
5
|
+
|
6
|
+
def draw_paths_to_visible_renderable_nodes(starting_node:)
|
7
|
+
recursively_cartograph_visible_renderable_nodes(node: starting_node)
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def recursively_cartograph_visible_renderable_nodes(node:, node_path: "", result: {})
|
13
|
+
node.child_nodes.each do |child_node_id, child_node|
|
14
|
+
child_node_path = iterate_on_node_path(node_path: node_path,
|
15
|
+
node_id: child_node_id)
|
16
|
+
|
17
|
+
if @bouncer.user_can_access?(child_node)
|
18
|
+
include_node_in_result_if_renderable(node: child_node,
|
19
|
+
result: result,
|
20
|
+
node_path: child_node_path)
|
21
|
+
|
22
|
+
unless child_node.leaf_node?
|
23
|
+
recursively_cartograph_visible_renderable_nodes(node: child_node,
|
24
|
+
node_path: child_node_path,
|
25
|
+
result: result)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
result
|
30
|
+
end
|
31
|
+
|
32
|
+
def iterate_on_node_path(node_path:, node_id:)
|
33
|
+
if node_path.empty?
|
34
|
+
node_id.to_s
|
35
|
+
else
|
36
|
+
"#{node_path}/#{node_id}"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def include_node_in_result_if_renderable(node:, node_path:, result:)
|
41
|
+
if node.can_be_rendered?
|
42
|
+
result[node_path] = nobilizer.bestow_title(node_path)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def nobilizer
|
47
|
+
@nobilizer ||= Guide::Nobilizer.new
|
48
|
+
end
|
49
|
+
end
|