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