dat_gretel 4.0.1

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 (76) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +24 -0
  3. data/.travis.yml +16 -0
  4. data/CHANGELOG.md +56 -0
  5. data/Gemfile +23 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +359 -0
  8. data/Rakefile +10 -0
  9. data/dat_gretel.gemspec +22 -0
  10. data/gemfiles/Gemfile-rails.3.1.x +19 -0
  11. data/gemfiles/Gemfile-rails.3.2.x +19 -0
  12. data/gemfiles/Gemfile-rails.4.0.x +19 -0
  13. data/gemfiles/Gemfile-rails.4.1.x +19 -0
  14. data/gemfiles/Gemfile-rails.5.2.x +19 -0
  15. data/lib/dat_gretel.rb +82 -0
  16. data/lib/dat_gretel/crumb.rb +70 -0
  17. data/lib/dat_gretel/crumbs.rb +64 -0
  18. data/lib/dat_gretel/deprecated.rb +1 -0
  19. data/lib/dat_gretel/deprecated/default_style_key.rb +25 -0
  20. data/lib/dat_gretel/deprecated/layout.rb +15 -0
  21. data/lib/dat_gretel/deprecated/show_root_alone.rb +27 -0
  22. data/lib/dat_gretel/deprecated/yield_links.rb +44 -0
  23. data/lib/dat_gretel/link.rb +40 -0
  24. data/lib/dat_gretel/renderer.rb +259 -0
  25. data/lib/dat_gretel/resettable.rb +13 -0
  26. data/lib/dat_gretel/version.rb +3 -0
  27. data/lib/dat_gretel/view_helpers.rb +71 -0
  28. data/lib/generators/dat_gretel/USAGE +8 -0
  29. data/lib/generators/dat_gretel/install_generator.rb +12 -0
  30. data/lib/generators/dat_gretel/templates/breadcrumbs.rb +28 -0
  31. data/test/dat_gretel_test.rb +24 -0
  32. data/test/deprecated_test.rb +45 -0
  33. data/test/dummy/Rakefile +7 -0
  34. data/test/dummy/app/assets/javascripts/application.js +15 -0
  35. data/test/dummy/app/assets/stylesheets/application.css +13 -0
  36. data/test/dummy/app/controllers/application_controller.rb +3 -0
  37. data/test/dummy/app/helpers/application_helper.rb +5 -0
  38. data/test/dummy/app/mailers/.gitkeep +0 -0
  39. data/test/dummy/app/models/.gitkeep +0 -0
  40. data/test/dummy/app/models/issue.rb +3 -0
  41. data/test/dummy/app/models/project.rb +3 -0
  42. data/test/dummy/app/views/breadcrumbs/site.rb +3 -0
  43. data/test/dummy/config.ru +4 -0
  44. data/test/dummy/config/application.rb +14 -0
  45. data/test/dummy/config/boot.rb +10 -0
  46. data/test/dummy/config/breadcrumbs.rb +3 -0
  47. data/test/dummy/config/breadcrumbs/test_crumbs.rb +85 -0
  48. data/test/dummy/config/database.yml +25 -0
  49. data/test/dummy/config/environment.rb +5 -0
  50. data/test/dummy/config/environments/development.rb +2 -0
  51. data/test/dummy/config/environments/production.rb +2 -0
  52. data/test/dummy/config/environments/test.rb +2 -0
  53. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  54. data/test/dummy/config/initializers/inflections.rb +15 -0
  55. data/test/dummy/config/initializers/mime_types.rb +5 -0
  56. data/test/dummy/config/initializers/secret_token.rb +7 -0
  57. data/test/dummy/config/initializers/session_store.rb +8 -0
  58. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  59. data/test/dummy/config/locales/en.yml +5 -0
  60. data/test/dummy/config/routes.rb +11 -0
  61. data/test/dummy/db/migrate/20130122163007_create_projects.rb +9 -0
  62. data/test/dummy/db/migrate/20130122163051_create_issues.rb +10 -0
  63. data/test/dummy/db/schema.rb +28 -0
  64. data/test/dummy/lib/assets/.gitkeep +0 -0
  65. data/test/dummy/log/.gitkeep +0 -0
  66. data/test/dummy/public/404.html +26 -0
  67. data/test/dummy/public/422.html +26 -0
  68. data/test/dummy/public/500.html +25 -0
  69. data/test/dummy/public/favicon.ico +0 -0
  70. data/test/dummy/script/rails +6 -0
  71. data/test/fixtures/issues.yml +4 -0
  72. data/test/fixtures/projects.yml +3 -0
  73. data/test/gretel_test.rb +24 -0
  74. data/test/helper_methods_test.rb +552 -0
  75. data/test/test_helper.rb +15 -0
  76. metadata +193 -0
@@ -0,0 +1,40 @@
1
+ module DatGretel
2
+ class Link
3
+ attr_accessor :key, :text, :url, :options, :current
4
+
5
+ def initialize(key, text, url, options = {})
6
+ # Use accessors so plugins can override their behavior
7
+ self.key, self.text, self.url, self.options = key, text, url, options
8
+ self.current = false
9
+ end
10
+
11
+ # Sets current so +current?+ will return +true+.
12
+ def current!
13
+ @current = true
14
+ end
15
+
16
+ # Returns +true+ if this is the last link in the breadcrumb trail.
17
+ def current?
18
+ !!@current
19
+ end
20
+
21
+ # Enables accessors and predicate methods for values in the +options+ hash.
22
+ # This can be used to pass information to links when rendering breadcrumbs
23
+ # manually.
24
+ #
25
+ # link = Link.new(:my_crumb, "My Crumb", my_path, title: "Test Title", other_value: "Other")
26
+ # link.title? # => true
27
+ # link.title # => "Test Title"
28
+ # link.other_value? # => true
29
+ # link.other_value # => "Other"
30
+ # link.some_other? # => false
31
+ # link.some_other # => nil
32
+ def method_missing(method, *args, &block)
33
+ if method =~ /(.+)\?$/
34
+ options[$1.to_sym].present?
35
+ else
36
+ options[method]
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,259 @@
1
+ module DatGretel
2
+ class Renderer
3
+ DEFAULT_OPTIONS = {
4
+ style: :inline,
5
+ pretext: "",
6
+ posttext: "",
7
+ separator: "",
8
+ autoroot: true,
9
+ display_single_fragment: false,
10
+ link_current: false,
11
+ link_current_to_request_path: true,
12
+ semantic: false,
13
+ class: "breadcrumbs",
14
+ current_class: "current",
15
+ fragment_class: "",
16
+ pretext_class: "pretext",
17
+ posttext_class: "posttext",
18
+ id: nil
19
+ }
20
+
21
+ DEFAULT_STYLES = {
22
+ inline: { container_tag: :div, separator: " › " },
23
+ ol: { container_tag: :ol, fragment_tag: :li },
24
+ ul: { container_tag: :ul, fragment_tag: :li },
25
+ bootstrap: { container_tag: :ol, fragment_tag: :li, class: "breadcrumb", fragment_class: "breadcrumb-item", current_class: "breadcrumb-item active" },
26
+ foundation5: { container_tag: :ul, fragment_tag: :li, class: "breadcrumbs", current_class: "current" }
27
+ }
28
+
29
+ def initialize(context, breadcrumb_key, *breadcrumb_args)
30
+ @context = context
31
+ @breadcrumb_key = breadcrumb_key
32
+ @breadcrumb_args = breadcrumb_args
33
+ end
34
+
35
+ # Renders the breadcrumbs HTML.
36
+ def render(options)
37
+ options = options_for_render(options)
38
+ links = links_for_render(options)
39
+
40
+ LinkCollection.new(context, links, options)
41
+ end
42
+
43
+ # Yields links with applied options.
44
+ def yield_links(options = {})
45
+ yield render(options)
46
+ end
47
+
48
+ # Returns the parent breadcrumb.
49
+ def parent_breadcrumb(options = {})
50
+ render(options)[-2]
51
+ end
52
+
53
+ # Yields the parent breadcrumb if any.
54
+ def yield_parent_breadcrumb(options = {})
55
+ if parent = parent_breadcrumb(options)
56
+ yield parent
57
+ end
58
+ end
59
+
60
+ private
61
+
62
+ attr_reader :context, :breadcrumb_key, :breadcrumb_args
63
+
64
+ # Returns merged options for rendering breadcrumbs.
65
+ def options_for_render(options = {})
66
+ style = options_for_style(options[:style] || DEFAULT_OPTIONS[:style])
67
+ DEFAULT_OPTIONS.merge(style).merge(options)
68
+ end
69
+
70
+ # Returns options for the given +style_key+ and raises an exception if it's not found.
71
+ def options_for_style(style_key)
72
+ if style = self.class.styles[style_key]
73
+ style
74
+ else
75
+ raise ArgumentError, "Breadcrumbs style #{style_key.inspect} not found. Use any of #{self.class.styles.keys.inspect}."
76
+ end
77
+ end
78
+
79
+ # Array of links with applied options.
80
+ def links_for_render(options = {})
81
+ out = links.dup
82
+
83
+ # Handle autoroot
84
+ if options[:autoroot] && out.map(&:key).exclude?(:root) && DatGretel::Crumbs.crumb_defined?(:root)
85
+ out.unshift(*DatGretel::Crumb.new(context, :root).links)
86
+ end
87
+
88
+ # Set current link to actual path
89
+ if options[:link_current_to_request_path] && out.any? && request
90
+ out.last.url = request.fullpath
91
+ end
92
+
93
+ # Handle show root alone
94
+ if out.size == 1 && !options[:display_single_fragment]
95
+ out.shift
96
+ end
97
+
98
+ # Set last link to current
99
+ out.last.try(:current!)
100
+
101
+ out
102
+ end
103
+
104
+ # Array of links for the path of the breadcrumb.
105
+ # Also reloads the breadcrumb configuration if needed.
106
+ def links
107
+ @links ||= if @breadcrumb_key.present?
108
+ # Reload breadcrumbs configuration if needed
109
+ DatGretel::Crumbs.reload_if_needed
110
+
111
+ # Get breadcrumb set by the `breadcrumb` method
112
+ crumb = DatGretel::Crumb.new(context, breadcrumb_key, *breadcrumb_args)
113
+
114
+ # Links of first crumb
115
+ links = crumb.links.dup
116
+
117
+ # Get parent links
118
+ links.unshift(*parent_links_for(crumb))
119
+
120
+ links
121
+ else
122
+ []
123
+ end
124
+ end
125
+
126
+ # Returns parent links for the crumb.
127
+ def parent_links_for(crumb)
128
+ links = []
129
+ while crumb = crumb.parent
130
+ links.unshift(*crumb.links)
131
+ end
132
+ links
133
+ end
134
+
135
+ # Proxy to view context.
136
+ def method_missing(method, *args, &block)
137
+ context.send(method, *args, &block)
138
+ end
139
+
140
+ class << self
141
+ include Resettable
142
+
143
+ # Registers a style for later use.
144
+ #
145
+ # Gretel::Renderer.register_style :ul, { container_tag: :ul, fragment_tag: :li }
146
+ def register_style(style_key, options)
147
+ styles[style_key] = options
148
+ end
149
+
150
+ # Hash of registered styles.
151
+ def styles
152
+ @styles ||= DEFAULT_STYLES
153
+ end
154
+ end
155
+
156
+ class LinkCollection < Array
157
+ attr_reader :context, :links, :options
158
+
159
+ def initialize(context, links, options = {})
160
+ @context, @links, @options = context, links, options
161
+ concat links
162
+ end
163
+
164
+ # Helper for returning all link keys to allow for simple testing.
165
+ def keys
166
+ map(&:key)
167
+ end
168
+
169
+ # Renders the links into breadcrumbs.
170
+ def render
171
+ return "" if links.empty?
172
+
173
+ # Loop through all but the last (current) link and build HTML of the fragments
174
+ fragments = links[0..-2].map do |link|
175
+ if options[:fragment_class].present?
176
+ render_fragment(options[:fragment_tag], link.text, link.url, options[:semantic], class: options[:fragment_class])
177
+ else
178
+ render_fragment(options[:fragment_tag], link.text, link.url, options[:semantic])
179
+ end
180
+ end
181
+
182
+ # The current link is handled a little differently, and is only linked if specified in the options
183
+ current_link = links.last
184
+ fragments << render_fragment(options[:fragment_tag], current_link.text, (options[:link_current] ? current_link.url : nil), options[:semantic], class: options[:current_class], current_link: current_link.url)
185
+
186
+ # Build the final HTML
187
+ html_fragments = []
188
+
189
+ if options[:pretext].present?
190
+ html_fragments << content_tag(:span, options[:pretext], class: options[:pretext_class])
191
+ end
192
+
193
+ html_fragments << fragments.join(options[:separator])
194
+
195
+ if options[:posttext].present?
196
+ html_fragments << content_tag(:span, options[:posttext], class: options[:posttext_class])
197
+ end
198
+
199
+ html = html_fragments.join(" ").html_safe
200
+ content_tag(options[:container_tag], html, id: options[:id], class: options[:class])
201
+ end
202
+
203
+ alias :to_s :render
204
+
205
+ # Renders HTML for a breadcrumb fragment, i.e. a breadcrumb link.
206
+ def render_fragment(fragment_tag, text, url, semantic, options = {})
207
+ if semantic
208
+ render_semantic_fragment(fragment_tag, text, url, options)
209
+ else
210
+ render_nonsemantic_fragment(fragment_tag, text, url, options)
211
+ end
212
+ end
213
+
214
+ # Renders semantic fragment HTML.
215
+ def render_semantic_fragment(fragment_tag, text, url, options = {})
216
+ if fragment_tag
217
+ text = content_tag(:span, text, itemprop: "title")
218
+
219
+ if url.present?
220
+ text = breadcrumb_link_to(text, url, itemprop: "url")
221
+ elsif options[:current_link].present?
222
+ current_url = "#{root_url}#{options[:current_link].gsub(/^\//, '')}"
223
+ text = text + tag(:meta, itemprop: "url", content: current_url)
224
+ end
225
+
226
+ content_tag(fragment_tag, text, class: options[:class], itemscope: "", itemtype: "http://data-vocabulary.org/Breadcrumb")
227
+ elsif url.present?
228
+ content_tag(:span, breadcrumb_link_to(content_tag(:span, text, itemprop: "title"), url, class: options[:class], itemprop: "url"), itemscope: "", itemtype: "http://data-vocabulary.org/Breadcrumb")
229
+ else
230
+ content_tag(:span, content_tag(:span, text, class: options[:class], itemprop: "title"), itemscope: "", itemtype: "http://data-vocabulary.org/Breadcrumb")
231
+ end
232
+ end
233
+
234
+ # Renders regular, non-semantic fragment HTML.
235
+ def render_nonsemantic_fragment(fragment_tag, text, url, options = {})
236
+ if fragment_tag
237
+ text = breadcrumb_link_to(text, url) if url.present?
238
+ content_tag(fragment_tag, text, class: options[:class])
239
+ elsif url.present?
240
+ breadcrumb_link_to(text, url, class: options[:class])
241
+ elsif options[:class].present?
242
+ content_tag(:span, text, class: options[:class])
243
+ else
244
+ text
245
+ end
246
+ end
247
+
248
+ # Proxy for +context.link_to+ that can be overridden by plugins.
249
+ def breadcrumb_link_to(name, url, options = {})
250
+ context.link_to(name, url, options)
251
+ end
252
+
253
+ # Proxy to view context.
254
+ def method_missing(method, *args, &block)
255
+ context.send(method, *args, &block)
256
+ end
257
+ end
258
+ end
259
+ end
@@ -0,0 +1,13 @@
1
+ module DatGretel
2
+ module Resettable
3
+ # Resets all instance variables and calls +reset!+ on all child modules and
4
+ # classes. Used for testing.
5
+ def reset!
6
+ instance_variables.each { |var| remove_instance_variable var }
7
+ constants.each do |c|
8
+ c = const_get(c)
9
+ c.reset! if c.respond_to?(:reset!)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,3 @@
1
+ module DatGretel
2
+ VERSION = "4.0.1"
3
+ end
@@ -0,0 +1,71 @@
1
+ module DatGretel
2
+ module ViewHelpers
3
+ # Sets the current breadcrumb to be rendered elsewhere. Put it somewhere in the view, preferably in the top, before you render any breadcrumbs HTML:
4
+ #
5
+ # <% breadcrumb :category, @category %>
6
+ #
7
+ # If you pass an instance of an object that responds to +model_name+ (like an ActiveRecord model instance), the breadcrumb can be automatically inferred, so a shortcut for the above would be:
8
+ #
9
+ # <% breadcrumb @category %>
10
+ def breadcrumb(key = nil, *args)
11
+ if key.nil? || key.is_a?(Hash)
12
+ raise ArgumentError, "The `breadcrumb` method was called with #{key.inspect} as the key. This method is used to set the breadcrumb. Maybe you meant to call the `breadcrumbs` method (with an 's' in the end) which is used to render the breadcrumbs?"
13
+ end
14
+ @_gretel_renderer = DatGretel::Renderer.new(self, key, *args)
15
+ end
16
+
17
+ # Yields a block where inside the block you have a different breadcrumb than outside.
18
+ #
19
+ # <% breadcrumb :about %>
20
+ #
21
+ # <%= breadcrumbs # shows the :about breadcrumb %>
22
+ #
23
+ # <% with_breadcrumb :product, Product.first do %>
24
+ # <%= breadcrumbs # shows the :product breadcrumb %>
25
+ # <% end %>
26
+ #
27
+ # <%= breadcrumbs # shows the :about breadcrumb %>
28
+ def with_breadcrumb(key, *args, &block)
29
+ original_renderer = @_gretel_renderer
30
+ @_gretel_renderer = DatGretel::Renderer.new(self, key, *args)
31
+ yield
32
+ @_gretel_renderer = original_renderer
33
+ end
34
+
35
+ # Renders the breadcrumbs HTML, for example in your layout. See the readme for default options.
36
+ # <%= breadcrumbs pretext: "You are here: " %>
37
+ #
38
+ # If you supply a block, it will yield an array with the breadcrumb links so you can build the breadcrumbs HTML manually:
39
+ # <% breadcrumbs do |links| %>
40
+ # <% if links.any? %>
41
+ # You are here:
42
+ # <% links.each do |link| %>
43
+ # <%= link_to link.text, link.url %> (<%= link.key %>)
44
+ # <% end %>
45
+ # <% end %>
46
+ # <% end %>
47
+ def breadcrumbs(options = {})
48
+ gretel_renderer.render(options)
49
+ end
50
+
51
+ # Returns or yields parent breadcrumb (second-to-last in the trail) if it is present.
52
+ #
53
+ # <% parent_breadcrumb do |link| %>
54
+ # <%= link_to link.text, link.url %> (<%= link.key %>)
55
+ # <% end %>
56
+ def parent_breadcrumb(options = {}, &block)
57
+ if block_given?
58
+ gretel_renderer.yield_parent_breadcrumb(options, &block)
59
+ else
60
+ gretel_renderer.parent_breadcrumb(options)
61
+ end
62
+ end
63
+
64
+ private
65
+
66
+ # Reference to the Gretel breadcrumbs renderer.
67
+ def gretel_renderer
68
+ @_gretel_renderer ||= DatGretel::Renderer.new(self, nil)
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,8 @@
1
+ Description:
2
+ Generates a sample breadcrumbs configuration file for Gretel.
3
+
4
+ Example:
5
+ rails generate dat_gretel:install
6
+
7
+ This will create:
8
+ config/breadcrumbs.rb
@@ -0,0 +1,12 @@
1
+ require 'rails/generators'
2
+
3
+ module DatGretel
4
+ class InstallGenerator < Rails::Generators::Base
5
+ source_root File.expand_path('../templates', __FILE__)
6
+
7
+ desc "Creates a sample configuration file in config/breadcrumbs.rb"
8
+ def create_config_file
9
+ copy_file "breadcrumbs.rb", "config/breadcrumbs.rb"
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,28 @@
1
+ crumb :root do
2
+ link "Home", root_path
3
+ end
4
+
5
+ # crumb :projects do
6
+ # link "Projects", projects_path
7
+ # end
8
+
9
+ # crumb :project do |project|
10
+ # link project.name, project_path(project)
11
+ # parent :projects
12
+ # end
13
+
14
+ # crumb :project_issues do |project|
15
+ # link "Issues", project_issues_path(project)
16
+ # parent :project, project
17
+ # end
18
+
19
+ # crumb :issue do |issue|
20
+ # link issue.title, issue_path(issue)
21
+ # parent :project_issues, issue.project
22
+ # end
23
+
24
+ # If you want to split your breadcrumbs configuration over multiple files, you
25
+ # can create a folder named `config/breadcrumbs` and put your configuration
26
+ # files there. All *.rb files (e.g. `frontend.rb` or `products.rb`) in that
27
+ # folder are loaded and reloaded automatically when you change them, just like
28
+ # this file (`config/breadcrumbs.rb`).
@@ -0,0 +1,24 @@
1
+ require 'test_helper'
2
+
3
+ class DatGretelTest < ActiveSupport::TestCase
4
+ setup do
5
+ DatGretel.reset!
6
+ end
7
+
8
+ test "defaults" do
9
+ assert_equal [Rails.root.join("config", "breadcrumbs.rb"),
10
+ Rails.root.join("config", "breadcrumbs", "**", "*.rb"),
11
+ Rails.root.join("app", "views", "breadcrumbs", "**", "*.rb")],
12
+ DatGretel.breadcrumb_paths[-3..-1]
13
+ assert_equal ["development"], DatGretel.reload_environments
14
+ assert !DatGretel.suppress_deprecation_warnings?
15
+ end
16
+
17
+ test "configuration block" do
18
+ DatGretel.configure do |config|
19
+ config.reload_environments << "staging"
20
+ end
21
+
22
+ assert_equal ["development", "staging"], DatGretel.reload_environments
23
+ end
24
+ end