ducktrails 0.0.1 → 0.0.3

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