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.
Files changed (83) hide show
  1. checksums.yaml +5 -5
  2. data/LICENSE +22 -0
  3. data/Rakefile +18 -0
  4. data/app/assets/javascripts/guide/application.js +21723 -0
  5. data/app/assets/javascripts/guide/scenario.js +12642 -0
  6. data/app/assets/stylesheets/guide/application.css +141 -0
  7. data/app/controllers/guide/base_controller.rb +83 -0
  8. data/app/controllers/guide/nodes_controller.rb +40 -0
  9. data/app/controllers/guide/scenarios_controller.rb +44 -0
  10. data/app/helpers/guide/application_helper.rb +4 -0
  11. data/app/helpers/guide/document_helper.rb +20 -0
  12. data/app/models/guide/arborist.rb +44 -0
  13. data/app/models/guide/bouncer.rb +34 -0
  14. data/app/models/guide/cartographer.rb +49 -0
  15. data/app/models/guide/content.rb +28 -0
  16. data/app/models/guide/default_authentication_system.rb +13 -0
  17. data/app/models/guide/default_authorisation_system.rb +18 -0
  18. data/app/models/guide/diplomat.rb +60 -0
  19. data/app/models/guide/document.rb +20 -0
  20. data/app/models/guide/endpoint_stocktaker.rb +94 -0
  21. data/app/models/guide/errors.rb +2 -0
  22. data/app/models/guide/errors/base.rb +6 -0
  23. data/app/models/guide/errors/interface_violation.rb +2 -0
  24. data/app/models/guide/errors/invalid_node.rb +2 -0
  25. data/app/models/guide/errors/invalid_scenario.rb +2 -0
  26. data/app/models/guide/errors/invalid_visibility_option.rb +2 -0
  27. data/app/models/guide/errors/permission_denied.rb +2 -0
  28. data/app/models/guide/fixture.rb +15 -0
  29. data/app/models/guide/fixtures.rb +3 -0
  30. data/app/models/guide/form_object.rb +3 -0
  31. data/app/models/guide/monkey.rb +47 -0
  32. data/app/models/guide/nobilizer.rb +9 -0
  33. data/app/models/guide/node.rb +67 -0
  34. data/app/models/guide/photographer.rb +20 -0
  35. data/app/models/guide/scout.rb +59 -0
  36. data/app/models/guide/simulator.rb +26 -0
  37. data/app/models/guide/structure.rb +75 -0
  38. data/app/models/guide/view_model.rb +37 -0
  39. data/app/view_models/guide/layout_view.rb +81 -0
  40. data/app/view_models/guide/navigation_view.rb +25 -0
  41. data/app/view_models/guide/node_view.rb +59 -0
  42. data/app/view_models/guide/scenario_layout_view.rb +49 -0
  43. data/app/view_models/guide/scenario_view.rb +39 -0
  44. data/app/views/guide/_content.html.erb +17 -0
  45. data/app/views/guide/common/_category.html.erb +9 -0
  46. data/app/views/guide/common/_footer.html.erb +21 -0
  47. data/app/views/guide/common/_locale_switcher.html.erb +6 -0
  48. data/app/views/guide/common/_navigation.html.erb +12 -0
  49. data/app/views/guide/common/_navigation_node.html.erb +39 -0
  50. data/app/views/guide/common/_page_title.html.erb +8 -0
  51. data/app/views/guide/common/_search.html.erb +23 -0
  52. data/app/views/guide/common/_unsupported_browser_message.html.erb +6 -0
  53. data/app/views/guide/common/_visibility_banner.html.erb +25 -0
  54. data/app/views/guide/nodes/_document.html.erb +3 -0
  55. data/app/views/guide/nodes/_scenario.html.erb +14 -0
  56. data/app/views/guide/nodes/_scenario_list.html.erb +10 -0
  57. data/app/views/guide/nodes/_structure.html.erb +26 -0
  58. data/app/views/guide/nodes/_template_location.html.erb +7 -0
  59. data/app/views/guide/nodes/show.html.erb +7 -0
  60. data/app/views/guide/scenarios/_scenario.html.erb +25 -0
  61. data/app/views/guide/scenarios/scenario/_locale_switcher.html.erb +5 -0
  62. data/app/views/guide/scenarios/scenario/_toolbar.html.erb +39 -0
  63. data/app/views/guide/scenarios/scenario/_visibility.html.erb +10 -0
  64. data/app/views/guide/scenarios/show.html.erb +1 -0
  65. data/app/views/layouts/guide/application.html.erb +76 -0
  66. data/app/views/layouts/guide/scenario.html.erb +38 -0
  67. data/app/views/layouts/guide/scenario/default.html.erb +1 -0
  68. data/app/views/layouts/guide/scenario/default.text.erb +1 -0
  69. data/config/initializers/assets.rb +1 -0
  70. data/config/locales/models/guide/monkey.en.yml +6 -0
  71. data/config/locales/views/guide/nodes/_structure.en.yml +7 -0
  72. data/config/routes.rb +6 -0
  73. data/lib/guide.rb +12 -0
  74. data/lib/guide/authorisation_spec_helper.rb +83 -0
  75. data/lib/guide/configuration.rb +29 -0
  76. data/lib/guide/consistency_spec_helper.rb +33 -0
  77. data/lib/guide/engine.rb +9 -0
  78. data/lib/guide/version.rb +3 -0
  79. data/lib/tasks/guide_tasks.rake +4 -0
  80. metadata +207 -16
  81. data/.gitignore +0 -26
  82. data/Gemfile +0 -4
  83. 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,4 @@
1
+ module Guide::ApplicationHelper
2
+ module DefaultInclude; end
3
+ include Guide.configuration.helper_module_to_globally_include.constantize
4
+ 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