ducktrails 0.0.1 → 0.0.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fbf690b1296a9de6e0cf9e70843900c366836428
4
- data.tar.gz: b69671826c7dd007ef7688647a1c65d406625eea
3
+ metadata.gz: df465aae8845fba74f32ed951aee5d7cc9509e36
4
+ data.tar.gz: 0b7fc7bd4dd5b4a8241e398bad0e6760ed2d754b
5
5
  SHA512:
6
- metadata.gz: fb63bd77873ac042ec08435c11e2a68ea0cac911bf20e3c8a5fd1ffe8e4e34b4d9776324978c12c9d22542401bd990006a3989d108abce45b9c623e8c989db77
7
- data.tar.gz: c054b643724161eccb18480ece94c1da7efa41fecc3167befb96c11a06467c037eb2550300c82e55e4d86261bcf77a4128ef852942fc0ab83610c6758df9e72b
6
+ metadata.gz: d7c3328e29ba82ee8bab8d429d68d2db8649bc26857f7d163296967756ce6a9060fd9ad893a32314f7147b921da96af628b48a238cfe90114c09bf89b4b659d4
7
+ data.tar.gz: '02539d3d583729da538d27cefe76c9f83bb1fbc580bf8d8b782cef98cb1f85f7b4e4921ba3c2abfbf6ea895a0cf99efee4ce79cbc0f4aaecce8fed8b51da984c'
data/README.md CHANGED
@@ -10,11 +10,40 @@ Automatically generates breadcrumbs based on routes.
10
10
  For basic applications using a restful architecture, ducktrails should generate the breadcrumbs automatically.
11
11
 
12
12
  For example, if the uri is `/users/#{user-id}/posts/#{post-id}`
13
+ Putting `<%= breadcrumbs %>` in the (erb) view.
13
14
 
14
15
  Would render:
15
16
 
16
17
  `Home / All Users / user-name / All Posts / post-name`
17
18
 
19
+ Simply include `= breadcrumbs` in your view. `breadcrumbs` takes a block argument. The block isn't required but if you want to set some objects for the uri resources (recommend) you can follow the guide below.
20
+
21
+ Resources are inferred and will be used to manipulate each URI segment. If a resource is not found, Ducktrails will fallback to the URI.
22
+
23
+ Possible keys for a breadcrumb resource are: `resource, key, policy, collection_prefix`. The key and collection prefix can be configured in the `Ducktrails.rb` initializer described below.
24
+
25
+ ```erb
26
+ <%=breadcrumbs do
27
+ {
28
+ users: {
29
+ resource: @user,
30
+ key: :first_name,
31
+ collection_prefix: 'Some'
32
+ }
33
+ }
34
+ end
35
+ %>
36
+ ```
37
+
38
+ Will output
39
+
40
+ Action | Breadcrumb
41
+ ---|---
42
+ index |`Home / Some Users`
43
+ show |`Home / Some Users / Kevin`
44
+ new |`Home / Some Users / New`
45
+ edit |`Home /Some Users / Kevin / edit`
46
+
18
47
  ## Installation
19
48
  Add this line to your application's Gemfile:
20
49
 
@@ -32,6 +61,20 @@ Or install it yourself as:
32
61
  $ gem install ducktrails
33
62
  ```
34
63
 
64
+ ## Configuration
65
+ Ducktrails provides a standard initializer file:
66
+ **Note**: Currently `ducktrails` is the only template theme provided
67
+
68
+ ```ruby
69
+ Ducktrails.configure do |config|
70
+ config.root_path = '/'
71
+ config.home_name = 'Home'
72
+ config.collection_prefix = 'All'
73
+ config.default_key = :id
74
+ config.theme = 'ducktrails'
75
+ end
76
+ ```
77
+
35
78
  ## Contributing
36
79
 
37
80
  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.
@@ -1,7 +1,9 @@
1
1
  <div class="breadcrumb-container">
2
2
  <div class="container">
3
3
  <ol class="breadcrumb">
4
- <li><%= home_crumb %></li>
4
+ <li>
5
+ <%= link_to(Ducktrails.config.home_name, Ducktrails.config.root_path) %>
6
+ </li>
5
7
  <% links.each do |link| %>
6
8
  <%= render partial: 'ducktrails/crumb_link', locals: { link: link } %>
7
9
  <% end %>
data/lib/ducktrails.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  module Ducktrails
2
+ VALID_RESOURCES = %i(resource collection_prefix as policy key).freeze
2
3
  end
3
4
 
4
5
  begin
@@ -11,34 +11,24 @@ module Ducktrails
11
11
 
12
12
  # Global settings for Ducktrails
13
13
  def self.config
14
- @config
14
+ @config ||= self.config
15
15
  end
16
16
 
17
- class Configuration #:nodoc:
17
+ class Configuration
18
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
19
 
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
20
+ config_accessor :theme,
21
+ :home_name,
22
+ :root_path,
23
+ :collection_prefix,
24
+ :default_key
34
25
  end
35
26
 
36
- # this is ugly. why can't we pass the default value to config_accessor...?
37
27
  configure do |config|
38
- config.root_path = '/'
28
+ config.theme = 'ducktrails'
39
29
  config.home_name = 'Home'
30
+ config.root_path = '/'
40
31
  config.collection_prefix = 'All'
41
32
  config.default_key = :id
42
- config.fallback_to_uri = true
43
33
  end
44
34
  end
@@ -3,108 +3,60 @@ require 'concerns/configurable'
3
3
  DEFAULTS = {
4
4
  key: :name,
5
5
  policy: true
6
- }
6
+ }.freeze
7
7
 
8
8
  module Ducktrails
9
- class LinkCollection < Array
9
+ class LinkCollection
10
10
  include Configurable
11
11
 
12
- attr_accessor :resources, :current_uri, :request
12
+ attr_accessor :resources, :current_uri
13
13
 
14
- def initialize(resources, current_uri, request)
15
- @resources ||= set_default_resources(resources)
14
+ def initialize(resources, current_uri)
15
+ @resources ||= default_resources(resources)
16
16
  @current_uri ||= current_uri
17
- @request ||= request
18
17
  end
19
18
 
20
19
  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
20
  split_uri.inject([]) do |links, uri_resource|
38
- links << build_uri_link(uri_resource)
21
+ links << Resource.new(resources, uri_resource, current_uri)
22
+ .link
39
23
  end.compact
40
24
  end
41
25
 
42
- def build_link(text, uri)
43
- {
44
- text: text,
45
- uri: uri
46
- }
47
- end
26
+ private
48
27
 
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
28
+ def split_uri
29
+ @split_uri ||= current_uri.split('/').reject(&:empty?)
63
30
  end
64
31
 
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)
32
+ def default_resources(yield_resources = nil)
33
+ return {} if yield_resources.nil?
34
+ yield_resources.inject({}) do |resources, resource|
35
+ resource[1].assert_valid_keys(Ducktrails::VALID_RESOURCES)
36
+ @resource = resource[1]
37
+ resources.merge(
38
+ resource[0] => DEFAULTS.merge(sanitized_resource)
39
+ )
80
40
  end
81
41
  end
82
42
 
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?)
43
+ # Used to filter and sanitize decorated resources
44
+ def sanitized_resource
45
+ return @resource if @resource.is_a?(String)
46
+ return unobjectified_resource if resource_decorated?
47
+ @resource
94
48
  end
95
49
 
96
- def action
97
- @action ||= request_pattern[:action]
50
+ def unobjectified_resource
51
+ @resource[:resource] = @resource[:resource].object
52
+ @resource
98
53
  end
99
54
 
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
55
+ def resource_decorated?
56
+ @resource[:resource].respond_to?(:decorated?) &&
57
+ @resource[:resource].decorated?
58
+ rescue NoMethodError
59
+ @resource
108
60
  end
109
61
  end
110
62
  end
@@ -0,0 +1,116 @@
1
+ require_relative 'utils/deep_struct'
2
+ # TODO: below
3
+ # 1. Too many conflicting name-spaces remaining from the first version
4
+ # 2. Class doesn't do 1 thing
5
+ # 3. Some redundancy between methods
6
+
7
+ module Ducktrails
8
+ class Resource
9
+ include Configurable
10
+
11
+ attr_reader :resources, :resource, :current_uri
12
+
13
+ def initialize(resources, resource, current_uri)
14
+ @resources = DeepStruct.new(resources)
15
+ @resource = resource
16
+ @current_uri = current_uri
17
+ end
18
+
19
+ def link
20
+ # Build resource link
21
+ # example /institutions/:institution_id/iterations/:id
22
+ if resources.send(resource.to_sym).present?
23
+ return unless resources[resource.to_sym][:policy]
24
+ build_link(link_text(resources[resource.to_sym]))
25
+ elsif (show_action || new_action || edit_action) && current_resource.present?
26
+ return unless current_resource[:policy]
27
+ build_link(link_text(current_resource))
28
+ else
29
+ # build links based off of uri
30
+ build_link(link_text(resource.underscore.titleize))
31
+ end
32
+ rescue ActionController::RoutingError
33
+ nil
34
+ end
35
+
36
+ private
37
+
38
+ def split_uri
39
+ @split_uri ||= current_uri.split('/').reject(&:empty?)
40
+ end
41
+
42
+ def uri_index
43
+ split_uri.index(resource)
44
+ end
45
+
46
+ # TODO: Refactor this to use a resource given by the view_helpers?
47
+ def current_resource
48
+ resources[request_pattern[:controller].split('/').last.to_sym]
49
+ end
50
+
51
+ def build_link(text)
52
+ {
53
+ text: text,
54
+ uri: progressive_uri
55
+ }
56
+ end
57
+
58
+ def progressive_uri
59
+ split_uri[0..uri_index].join('/').prepend('/')
60
+ end
61
+
62
+ def link_text(resource)
63
+ return string_resource if resource.is_a?(String)
64
+
65
+ if resource.nil? || resource.resource.nil?
66
+ return resource.as.prepend(col_prefix) unless resource.resource.present? || resource.as.nil?
67
+ return controller_resource_title
68
+ end
69
+
70
+ case request_pattern(progressive_uri)[:action]
71
+ when 'show'
72
+ resource[:resource].send(resource[:key])
73
+ when 'edit'
74
+ 'Edit ' + resource[:resource].send(resource[:key])
75
+ when 'new'
76
+ 'New ' + resourcer(resource.try(:resource)).singularize
77
+ else
78
+ resourcer(resource.try(:resource))
79
+ end
80
+ end
81
+
82
+ def string_resource
83
+ return resource.titleize if show_action || edit_action || new_action
84
+ resource.titleize.prepend(col_prefix)
85
+ end
86
+
87
+ def resourcer(resource)
88
+ resource.class.name.split('::').first.underscore.titleize.pluralize.prepend(col_prefix)
89
+ end
90
+
91
+ def request_pattern(url = nil)
92
+ @request_pattern ||= Rails.application.routes.recognize_path(url || current_uri)
93
+ end
94
+
95
+ def controller_resource_title
96
+ return string_resource if request_pattern[:controller].split('/')[uri_index].nil?
97
+ request_pattern[:controller].split('/')[uri_index].underscore.titleize.pluralize.
98
+ prepend(col_prefix)
99
+ end
100
+
101
+ def col_prefix
102
+ return '' if new_action || edit_action
103
+ if resources[resource]&.collection_prefix.present?
104
+ resources[resource].collection_prefix.concat(' ')
105
+ else
106
+ collection_prefix
107
+ end
108
+ end
109
+
110
+ %w(index show edit new).each do |action_name|
111
+ define_method("#{action_name}_action") do
112
+ request_pattern(progressive_uri)[:action].eql?(action_name)
113
+ end
114
+ end
115
+ end
116
+ end
@@ -15,8 +15,8 @@ module Ducktrails
15
15
 
16
16
  def partial_path
17
17
  [
18
- 'ducktrails',
19
- self.class.name.demodulize.underscore
18
+ Ducktrails.config.theme,
19
+ self.class.name.demodulize.underscore
20
20
  ].compact.join("/")
21
21
  end
22
22
  end
@@ -0,0 +1,18 @@
1
+ class DeepStruct < OpenStruct
2
+ def initialize(hash = nil)
3
+ @table = {}
4
+ @hash_table = {}
5
+
6
+ return unless hash
7
+ hash.each do |k, v|
8
+ @table[k.to_sym] = (v.is_a?(Hash) ? self.class.new(v) : v)
9
+ @hash_table[k.to_sym] = v
10
+
11
+ new_ostruct_member(k)
12
+ end
13
+ end
14
+
15
+ def to_h
16
+ @hash_table
17
+ end
18
+ end
@@ -1,3 +1,3 @@
1
1
  module Ducktrails
2
- VERSION = '0.0.1'
2
+ VERSION = '0.0.3'
3
3
  end
@@ -5,23 +5,18 @@ require 'ducktrails/tags'
5
5
  module Ducktrails
6
6
  module ViewHelpers
7
7
  class Breadcrumber < Tag
8
-
9
8
  def initialize(options = {})
10
9
  @template = options[:template]
11
- @output_buffer = ActionView::OutputBuffer.new
12
- end
13
-
14
- def render(&block)
15
- @output_buffer
16
10
  end
17
11
  end
18
12
 
19
- def breadcrumbs(options = {})
20
- ducktrail_render(LinkCollection.new(yield, current_uri, current_request).links)
13
+ def breadcrumbs(&block)
14
+ resources = build_resource(&block)
15
+ ducktrail_render(LinkCollection.new(resources, current_uri).links)
21
16
  end
22
17
 
23
- def home_crumb(options = {})
24
- link_to(Ducktrails.config.home_name, '/')
18
+ def build_resource(&block)
19
+ block_given? ? yield : nil
25
20
  end
26
21
 
27
22
  private
@@ -30,10 +25,6 @@ module Ducktrails
30
25
  request.fullpath
31
26
  end
32
27
 
33
- def current_request
34
- request
35
- end
36
-
37
28
  def current_action
38
29
  Rails.application.routes.recognize_path(current_uri)[:action]
39
30
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ducktrails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kevin Brown
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-07-18 00:00:00.000000000 Z
11
+ date: 2018-02-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -25,7 +25,7 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '4.1'
27
27
  - !ruby/object:Gem::Dependency
28
- name: pg
28
+ name: factory_girl_rails
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
@@ -39,7 +39,21 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: capybara
42
+ name: mocha
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
+ - !ruby/object:Gem::Dependency
56
+ name: sqlite3
43
57
  requirement: !ruby/object:Gem::Requirement
44
58
  requirements:
45
59
  - - ">="
@@ -62,7 +76,6 @@ files:
62
76
  - MIT-LICENSE
63
77
  - README.md
64
78
  - Rakefile
65
- - app/config/routes.rb
66
79
  - app/views/ducktrails/_breadcrumber.html.erb
67
80
  - app/views/ducktrails/_crumb_link.html.erb
68
81
  - lib/ducktrails.rb
@@ -70,7 +83,9 @@ files:
70
83
  - lib/ducktrails/config.rb
71
84
  - lib/ducktrails/engine.rb
72
85
  - lib/ducktrails/link_collection.rb
86
+ - lib/ducktrails/resource.rb
73
87
  - lib/ducktrails/tags.rb
88
+ - lib/ducktrails/utils/deep_struct.rb
74
89
  - lib/ducktrails/version.rb
75
90
  - lib/ducktrails/view_helpers.rb
76
91
  homepage: http://github.com/proctoru/ducktrails
data/app/config/routes.rb DELETED
@@ -1,2 +0,0 @@
1
- Ducktrails::Engine.routes.draw do
2
- end