ducktrails 0.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.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +52 -0
- data/Rakefile +19 -0
- data/app/config/routes.rb +2 -0
- data/app/views/ducktrails/_breadcrumber.html.erb +10 -0
- data/app/views/ducktrails/_crumb_link.html.erb +3 -0
- data/lib/ducktrails/concerns/configurable.rb +12 -0
- data/lib/ducktrails/config.rb +44 -0
- data/lib/ducktrails/engine.rb +6 -0
- data/lib/ducktrails/link_collection.rb +110 -0
- data/lib/ducktrails/tags.rb +23 -0
- data/lib/ducktrails/version.rb +3 -0
- data/lib/ducktrails/view_helpers.rb +47 -0
- data/lib/ducktrails.rb +22 -0
- metadata +100 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: fbf690b1296a9de6e0cf9e70843900c366836428
|
4
|
+
data.tar.gz: b69671826c7dd007ef7688647a1c65d406625eea
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: fb63bd77873ac042ec08435c11e2a68ea0cac911bf20e3c8a5fd1ffe8e4e34b4d9776324978c12c9d22542401bd990006a3989d108abce45b9c623e8c989db77
|
7
|
+
data.tar.gz: c054b643724161eccb18480ece94c1da7efa41fecc3167befb96c11a06467c037eb2550300c82e55e4d86261bcf77a4128ef852942fc0ab83610c6758df9e72b
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2017 Kevin Brown
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# 🦆 Ducktrails
|
2
|
+
|
3
|
+
Automatically generates breadcrumbs based on routes.
|
4
|
+
|
5
|
+
**Warning: This API is still alpha; we welcome PRs and ideas as issues.**
|
6
|
+
|
7
|
+
[](https://www.youtube.com/watch?v=CMU2NwaaXEA "Ducktails Theme")
|
8
|
+
|
9
|
+
## Usage
|
10
|
+
For basic applications using a restful architecture, ducktrails should generate the breadcrumbs automatically.
|
11
|
+
|
12
|
+
For example, if the uri is `/users/#{user-id}/posts/#{post-id}`
|
13
|
+
|
14
|
+
Would render:
|
15
|
+
|
16
|
+
`Home / All Users / user-name / All Posts / post-name`
|
17
|
+
|
18
|
+
## Installation
|
19
|
+
Add this line to your application's Gemfile:
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
gem 'ducktrails'
|
23
|
+
```
|
24
|
+
|
25
|
+
And then execute:
|
26
|
+
```bash
|
27
|
+
$ bundle
|
28
|
+
```
|
29
|
+
|
30
|
+
Or install it yourself as:
|
31
|
+
```bash
|
32
|
+
$ gem install ducktrails
|
33
|
+
```
|
34
|
+
|
35
|
+
## Contributing
|
36
|
+
|
37
|
+
Please refer to each project's style guidelines and guidelines for submitting patches and additions. In general, we follow the "fork-and-pull" Git workflow.
|
38
|
+
|
39
|
+
1. Fork the repo on GitHub
|
40
|
+
2. Clone the project to your own machine
|
41
|
+
1. `bundle`
|
42
|
+
2. Create the test db `bundle exec rake --rakefile test/dummy/Rakefile db:setup`
|
43
|
+
3. `bundle exec rake` to test.
|
44
|
+
3. Ensure your test coverage is A+
|
45
|
+
4. Commit changes to your own branch
|
46
|
+
5. Push your work back up to your fork
|
47
|
+
6. Submit a Pull request so that we can review your changes
|
48
|
+
|
49
|
+
NOTE: **Be sure to merge the latest from "upstream" before making a pull request!**
|
50
|
+
|
51
|
+
## License
|
52
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'bundler/gem_tasks'
|
8
|
+
|
9
|
+
require 'rake/testtask'
|
10
|
+
|
11
|
+
Rake::TestTask.new(:test) do |t|
|
12
|
+
t.libs << 'lib'
|
13
|
+
t.libs << 'test'
|
14
|
+
t.pattern = 'test/**/*_test.rb'
|
15
|
+
t.verbose = true
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
task default: :test
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'active_support/configurable'
|
2
|
+
|
3
|
+
module Ducktrails
|
4
|
+
# Configures global settings for Ducktrails
|
5
|
+
# Ducktrails.configure do |config|
|
6
|
+
# config.default_per_page = 10
|
7
|
+
# end
|
8
|
+
def self.configure(&block)
|
9
|
+
yield @config ||= Ducktrails::Configuration.new
|
10
|
+
end
|
11
|
+
|
12
|
+
# Global settings for Ducktrails
|
13
|
+
def self.config
|
14
|
+
@config
|
15
|
+
end
|
16
|
+
|
17
|
+
class Configuration #:nodoc:
|
18
|
+
include ActiveSupport::Configurable
|
19
|
+
config_accessor :root_path
|
20
|
+
config_accessor :home_name
|
21
|
+
# TODO: How to set this?
|
22
|
+
config_accessor :collection_prefix
|
23
|
+
config_accessor :default_key
|
24
|
+
config_accessor :fallback_to_uri
|
25
|
+
config
|
26
|
+
def param_name
|
27
|
+
config.param_name.respond_to?(:call) ? config.param_name.call : config.param_name
|
28
|
+
end
|
29
|
+
|
30
|
+
# define param_name writer (copied from AS::Configurable)
|
31
|
+
writer, line = 'def param_name=(value); config.param_name = value; end', __LINE__
|
32
|
+
singleton_class.class_eval writer, __FILE__, line
|
33
|
+
class_eval writer, __FILE__, line
|
34
|
+
end
|
35
|
+
|
36
|
+
# this is ugly. why can't we pass the default value to config_accessor...?
|
37
|
+
configure do |config|
|
38
|
+
config.root_path = '/'
|
39
|
+
config.home_name = 'Home'
|
40
|
+
config.collection_prefix = 'All'
|
41
|
+
config.default_key = :id
|
42
|
+
config.fallback_to_uri = true
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'concerns/configurable'
|
2
|
+
|
3
|
+
DEFAULTS = {
|
4
|
+
key: :name,
|
5
|
+
policy: true
|
6
|
+
}
|
7
|
+
|
8
|
+
module Ducktrails
|
9
|
+
class LinkCollection < Array
|
10
|
+
include Configurable
|
11
|
+
|
12
|
+
attr_accessor :resources, :current_uri, :request
|
13
|
+
|
14
|
+
def initialize(resources, current_uri, request)
|
15
|
+
@resources ||= set_default_resources(resources)
|
16
|
+
@current_uri ||= current_uri
|
17
|
+
@request ||= request
|
18
|
+
end
|
19
|
+
|
20
|
+
def links
|
21
|
+
build_links
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
# All the logic to build the links
|
27
|
+
# Should render an index & show if action is show
|
28
|
+
# Should render an index & new/edit if action is new/edit
|
29
|
+
# Should render an index as _current_page if action is index
|
30
|
+
# If resources are empty, generate breadcrumbs from the uri?
|
31
|
+
def build_links
|
32
|
+
return generate_uri_breadcrumbs if Ducktrails.config.fallback_to_uri
|
33
|
+
raise 'Please provide block configuration for breadcrumbs.'
|
34
|
+
end
|
35
|
+
|
36
|
+
def generate_uri_breadcrumbs
|
37
|
+
split_uri.inject([]) do |links, uri_resource|
|
38
|
+
links << build_uri_link(uri_resource)
|
39
|
+
end.compact
|
40
|
+
end
|
41
|
+
|
42
|
+
def build_link(text, uri)
|
43
|
+
{
|
44
|
+
text: text,
|
45
|
+
uri: uri
|
46
|
+
}
|
47
|
+
end
|
48
|
+
|
49
|
+
def build_uri_link(uri_segment)
|
50
|
+
index = split_uri.index(uri_segment)
|
51
|
+
if resources[uri_segment.to_sym].present?
|
52
|
+
return unless resources[uri_segment.to_sym][:policy]
|
53
|
+
# Build resource link
|
54
|
+
# example /institutions/:institution_id/iterations/:id
|
55
|
+
build_link(text_link(resources[uri_segment.to_sym], index.odd?), split_uri[0..index].join('/').prepend('/'))
|
56
|
+
elsif index.odd? && resources[split_uri[index - 1].to_sym].present?
|
57
|
+
return unless resources[split_uri[index - 1].to_sym][:policy]
|
58
|
+
build_link(text_link(resources[split_uri[index - 1].to_sym], index.odd?), split_uri[0..index].join('/').prepend('/'))
|
59
|
+
else
|
60
|
+
# build links based off of uri
|
61
|
+
build_link(text_link(uri_segment.underscore.humanize, index.odd?), split_uri[0..index].join('/').prepend('/'))
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# TODO factor out the resource & key to use config if key is nil
|
66
|
+
def text_link(resource, show_action = nil)
|
67
|
+
if resource.is_a?(String)
|
68
|
+
return resource if show_action
|
69
|
+
return resource.prepend(collection_prefix)
|
70
|
+
end
|
71
|
+
|
72
|
+
if resource[:resource].nil?
|
73
|
+
return request_pattern[:controller].split('/').last.underscore.humanize.pluralize.prepend(collection_prefix)
|
74
|
+
end
|
75
|
+
|
76
|
+
if show_action
|
77
|
+
return resource[:resource].send(resource[:key])
|
78
|
+
else
|
79
|
+
return resourcer(resource)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def resourcer(resource)
|
84
|
+
resource = resource[:resource].object if resource[:resource].respond_to?(:object)
|
85
|
+
resource.class.name.split('::').first.underscore.humanize.pluralize.prepend(collection_prefix)
|
86
|
+
end
|
87
|
+
|
88
|
+
def request_pattern
|
89
|
+
@request_pattern ||= Rails.application.routes.recognize_path(current_uri)
|
90
|
+
end
|
91
|
+
|
92
|
+
def split_uri
|
93
|
+
@split_uri ||= current_uri.split('/').reject(&:empty?)
|
94
|
+
end
|
95
|
+
|
96
|
+
def action
|
97
|
+
@action ||= request_pattern[:action]
|
98
|
+
end
|
99
|
+
|
100
|
+
def set_default_resources(yield_resources)
|
101
|
+
yield_resources.inject({}) do |resources, resource|
|
102
|
+
resources.merge(
|
103
|
+
{
|
104
|
+
resource[0] => DEFAULTS.merge(resource[1])
|
105
|
+
}
|
106
|
+
)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Ducktrails
|
2
|
+
class Tag
|
3
|
+
include ::ActionView::Context
|
4
|
+
|
5
|
+
def initialize(template, options = {})
|
6
|
+
@template = template
|
7
|
+
@options = options.dup
|
8
|
+
@params = template
|
9
|
+
@views_prefix = @options.delete(:views_prefix)
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_s(locals = {})
|
13
|
+
@template.render partial_path, links: locals[:links]
|
14
|
+
end
|
15
|
+
|
16
|
+
def partial_path
|
17
|
+
[
|
18
|
+
'ducktrails',
|
19
|
+
self.class.name.demodulize.underscore
|
20
|
+
].compact.join("/")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'action_view'
|
2
|
+
require 'action_view/context'
|
3
|
+
require 'ducktrails/tags'
|
4
|
+
|
5
|
+
module Ducktrails
|
6
|
+
module ViewHelpers
|
7
|
+
class Breadcrumber < Tag
|
8
|
+
|
9
|
+
def initialize(options = {})
|
10
|
+
@template = options[:template]
|
11
|
+
@output_buffer = ActionView::OutputBuffer.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def render(&block)
|
15
|
+
@output_buffer
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def breadcrumbs(options = {})
|
20
|
+
ducktrail_render(LinkCollection.new(yield, current_uri, current_request).links)
|
21
|
+
end
|
22
|
+
|
23
|
+
def home_crumb(options = {})
|
24
|
+
link_to(Ducktrails.config.home_name, '/')
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def current_uri
|
30
|
+
request.fullpath
|
31
|
+
end
|
32
|
+
|
33
|
+
def current_request
|
34
|
+
request
|
35
|
+
end
|
36
|
+
|
37
|
+
def current_action
|
38
|
+
Rails.application.routes.recognize_path(current_uri)[:action]
|
39
|
+
end
|
40
|
+
|
41
|
+
def ducktrail_render(links)
|
42
|
+
@_ducktrail_render ||= Breadcrumber.new(template: self).to_s(links: links)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
ActionView::Base.send :include, Ducktrails::ViewHelpers
|
47
|
+
end
|
data/lib/ducktrails.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
module Ducktrails
|
2
|
+
end
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rails'
|
6
|
+
rescue LoadError
|
7
|
+
#do nothing
|
8
|
+
end
|
9
|
+
|
10
|
+
$stderr.puts <<-EOC if !defined?(Rails)
|
11
|
+
warning: no framework detected.
|
12
|
+
|
13
|
+
Your Gemfile might not be configured properly.
|
14
|
+
---- e.g. ----
|
15
|
+
Rails:
|
16
|
+
gem 'ducktrails'
|
17
|
+
EOC
|
18
|
+
|
19
|
+
require 'ducktrails/config'
|
20
|
+
require 'ducktrails/engine'
|
21
|
+
require 'ducktrails/tags'
|
22
|
+
require 'ducktrails/view_helpers'
|
metadata
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ducktrails
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Kevin Brown
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-07-18 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '4.1'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '4.1'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: pg
|
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: capybara
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description: RESTful breadcrumbs
|
56
|
+
email:
|
57
|
+
- chevinbrown@gmail.com
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- MIT-LICENSE
|
63
|
+
- README.md
|
64
|
+
- Rakefile
|
65
|
+
- app/config/routes.rb
|
66
|
+
- app/views/ducktrails/_breadcrumber.html.erb
|
67
|
+
- app/views/ducktrails/_crumb_link.html.erb
|
68
|
+
- lib/ducktrails.rb
|
69
|
+
- lib/ducktrails/concerns/configurable.rb
|
70
|
+
- lib/ducktrails/config.rb
|
71
|
+
- lib/ducktrails/engine.rb
|
72
|
+
- lib/ducktrails/link_collection.rb
|
73
|
+
- lib/ducktrails/tags.rb
|
74
|
+
- lib/ducktrails/version.rb
|
75
|
+
- lib/ducktrails/view_helpers.rb
|
76
|
+
homepage: http://github.com/proctoru/ducktrails
|
77
|
+
licenses:
|
78
|
+
- MIT
|
79
|
+
metadata: {}
|
80
|
+
post_install_message:
|
81
|
+
rdoc_options: []
|
82
|
+
require_paths:
|
83
|
+
- lib
|
84
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: 2.0.0
|
89
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
requirements: []
|
95
|
+
rubyforge_project:
|
96
|
+
rubygems_version: 2.6.8
|
97
|
+
signing_key:
|
98
|
+
specification_version: 4
|
99
|
+
summary: 'Ducktrails: Breadcrumbs that are duckin'' awesome.'
|
100
|
+
test_files: []
|