hensel 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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 08c2d34b1ceac0afa538c237967ef7ce7fd6e9d8
4
+ data.tar.gz: 710a9707597f29eddc79e054bf4ce83b91277f9a
5
+ SHA512:
6
+ metadata.gz: d3bc09b7389bdefdc9f524bdd0f790bbde158629d057299f00991c226ea03fb2fc1e1040f4fc7701f22c1e252a4f3aa7ea7204fdf1027e3ebcfa24fe360d688b
7
+ data.tar.gz: ced520e15fd2121ae7bb279d2cad07bb4970762ea1cc8ddf2171ed4601cdcc393444c6c08ec013d18d8b9eec9fbb19d40b4e2a6f1a90d174d761b43aee1ab365
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
data/.travis.yml ADDED
@@ -0,0 +1,20 @@
1
+ lang: ruby
2
+ before_install: gem install bundler --pre
3
+ install:
4
+ - gem update --system
5
+ - bundle update
6
+ rvm:
7
+ - 2.0.0
8
+ - 2.1.0
9
+ - jruby-head
10
+ - rbx
11
+ notifications:
12
+ recipients:
13
+ - namusyaka@gmail.com
14
+ branches:
15
+ only:
16
+ - master
17
+ matrix:
18
+ allow_failures:
19
+ - rvm: rbx
20
+ - rvm: jruby-head
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in hensel.gemspec
4
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,13 @@
1
+ guard 'rspec', version: 2 do
2
+ watch(%r{^spec/.+_spec\.rb$})
3
+ watch(%r{^lib/(.+)\.rb$}) {|m| "spec/#{m[1]}_spec.rb" }
4
+ watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
5
+ watch('spec/spec_helper.rb') { "spec" }
6
+ end
7
+
8
+ # Add files and commands to this file, like the example:
9
+ # watch(%r{file/path}) { `command(s)` }
10
+ #
11
+ guard 'shell' do
12
+ watch(/(.*).txt/) {|m| `tail #{m[0]}` }
13
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 namusyaka
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,337 @@
1
+ # Hensel
2
+
3
+ [![Build Status](https://travis-ci.org/namusyaka/hensel.svg)](https://travis-ci.org/namusyaka/hensel)
4
+
5
+ Hensel makes it easy to build the breadcrumbs.
6
+
7
+ Especially, want to recommend for use in Sinatra and Padrino.
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ gem 'hensel'
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install hensel
22
+
23
+ ## Requirements
24
+
25
+ * MRI 2.0+
26
+
27
+ ## Overview
28
+
29
+ Hensel can be used easily in your web applications, and it has powerful flexibility.
30
+ You can use it as helper and builder.
31
+ In the next section, will explain in detail how to use them.
32
+
33
+ ## Usage
34
+
35
+ ### Configuration
36
+
37
+ ```ruby
38
+ require 'hensel'
39
+
40
+ Hensel.configure do |config|
41
+ # Default values
42
+ config.bootstrap = false
43
+ config.escape_html = true
44
+ config.indentation = true
45
+ config.last_item_link = false
46
+ config.richsnippet = :microdata
47
+ config.attr_wrapper = "'"
48
+ config.whitespace = " "
49
+ config.parent_element = :ul
50
+ end
51
+ ```
52
+
53
+ **If `bootstrap` is set to `true`, the parent element of breadcrumbs will contain `breadcrumb` as class attrbiute, and the last item will contain `active` as class attrbiute as well.**
54
+ It will be something like below.
55
+
56
+ ```html
57
+ <ul class='breadcrumb'>
58
+ <li>
59
+ <a href='/'>
60
+ index
61
+ </a>
62
+ </li>
63
+ <li class='active'>
64
+ current
65
+ </li>
66
+ </ul>
67
+ ```
68
+
69
+ **If `escape_html` is set to `true`, the text of item and all value of attributes will be escaped.**
70
+
71
+ **If `indentation` is set to `true`, the breadcrumbs will be indented.**
72
+
73
+ **If `richsnippet` is set to correct symbol, the breadcrumbs will follow the rules of the rich snippets that have been specified.**
74
+ There is a `:microdata` and `:rdfa`, please specify `nil` if not required type.
75
+ It will be something like below.
76
+
77
+ ```html
78
+ <!-- microdata -->
79
+ <ul>
80
+ <li itemscope itemtype='http://data-vocabulary.org/Breadcrumb'>
81
+ <a href='/' itemprop='url'>
82
+ <span itemprop='title'>
83
+ index
84
+ </span>
85
+ </a>
86
+ </li>
87
+ <li itemscope itemtype='http://data-vocabulary.org/Breadcrumb'>
88
+ <span itemprop='title'>
89
+ current
90
+ </span>
91
+ </li>
92
+ </ul>
93
+
94
+ <!-- RDFa -->
95
+ <ul xmlns:v='http://rdf.data-vocabulary.org/#'>
96
+ <li typeof='v:Breadcrumb'>
97
+ <a href='/' rel='v:url' property="v:title">
98
+ index
99
+ </a>
100
+ </li>
101
+ <li typeof='v:Breadcrumb'>
102
+ <span property="v:title">
103
+ current
104
+ </span>
105
+ </li>
106
+ </ul>
107
+ ```
108
+
109
+ *If don't have special reason, you should enable the option.*
110
+ *Microdata and RDFa are supported by [google](https://support.google.com/webmasters/answer/185417?hl=en).*
111
+
112
+
113
+ **If `last_item_link` is set to `true`, the link of the last item will contain `a` element as with other elements.**
114
+ It will be something below.
115
+
116
+ ```html
117
+ <!-- If `last_item_link` is set to `true` -->
118
+ <ul class="breadcrumb">
119
+ <li>
120
+ <a href="/">
121
+ index
122
+ </a>
123
+ </li>
124
+ <li>
125
+ <a href="/foo">
126
+ foo
127
+ </a>
128
+ </li>
129
+ </ul>
130
+ <!-- If `last_item_link` is set to `false` -->
131
+ <ul class="breadcrumb">
132
+ <li>
133
+ <a href="/">
134
+ index
135
+ </a>
136
+ </li>
137
+ <li>
138
+ <span>
139
+ foo
140
+ </span>
141
+ </li>
142
+ </ul>
143
+ ```
144
+
145
+
146
+ **If `attr_wrapper` is set to a wrapper string, all attributes will be used it as the attribute wrapper.**
147
+ It will be somthing below.
148
+
149
+ ```html
150
+ <!-- If `attr_wrapper` is set to `'"'` -->
151
+ <ul class="breadcrumb">
152
+ <li>
153
+ <a href="/">
154
+ index
155
+ </a>
156
+ </li>
157
+ </ul>
158
+ <!-- If `attr_wrapper` is set to `nil` -->
159
+ <ul class=breadcrumb>
160
+ <li>
161
+ <a href=/>
162
+ index
163
+ </a>
164
+ </li>
165
+ </ul>
166
+ ```
167
+
168
+ **If `whitespace` is set to a whitespace string, all indentation will be used it as the indentation space.**
169
+ It will be somthing below.
170
+
171
+ ```html
172
+ <!-- If `whitespace` is set to `" "` -->
173
+ <ul>
174
+ <li>
175
+ <a href="/">
176
+ index
177
+ </a>
178
+ </li>
179
+ </ul>
180
+ <!-- If `attr_wrapper` is set to `" "` -->
181
+ <ul>
182
+ <li>
183
+ <a href="/">
184
+ index
185
+ </a>
186
+ </li>
187
+ </ul>
188
+ ```
189
+
190
+ **If `parent_element` is set to a name string, it will be used as a name of the parent element.**
191
+
192
+ ### Builder
193
+
194
+ #### `add(text, url, **options) -> Hensel::Builder::Item`
195
+
196
+ Adds a new item to items, and returns a fresh instance of `Hensel::Builder::Item` built by the builder.
197
+
198
+ ```ruby
199
+ builder = Hensel::Builder.new
200
+ item = builder.add("home", "/")
201
+ item.text #=> "home"
202
+ item.url #=> "/"
203
+ ```
204
+
205
+ #### `add(**parameters) -> Hensel::Builder::Item`
206
+
207
+ ```ruby
208
+ builder = Hensel::Builder.new
209
+ item = builder.add(text: "home", url: "/")
210
+ item.text #=> "home"
211
+ item.url #=> "/"
212
+ ```
213
+
214
+ #### `remove(text)`
215
+
216
+ Removes the item from items.
217
+
218
+ ```ruby
219
+ builder = Hensel::Builder.new
220
+ builder.add(text: "home", url: "/")
221
+
222
+ builder.items.empty? #=> false
223
+ builder.remove("home")
224
+ builder.items.empty? #=> true
225
+ ```
226
+
227
+ #### `remove{|item| ... }`
228
+
229
+ ```ruby
230
+ builder = Hensel::Builder.new
231
+ builder.add(text: "home", url: "/")
232
+
233
+ builder.items.empty? #=> false
234
+ builder.remove{|item| item.text == "home" }
235
+ builder.items.empty? #=> true
236
+ ```
237
+
238
+ #### `render -> String`
239
+
240
+ Renders the breadcrumbs, and returns the html of breadcrumbs rendered by this method.
241
+
242
+ ```ruby
243
+ builder = Hensel::Builder.new
244
+ builder.add(text: "home", url: "/")
245
+
246
+ builder.render #=> "<ul> ... </ul>"
247
+ ```
248
+
249
+ #### `render{ ... } -> String`
250
+
251
+ This method is for customize breadcrumbs.
252
+
253
+ If this method has a parameter, it will be an instance of `Hensel::Builder::Item`.
254
+
255
+ If this method does not have parameter, the block will be evaluated as an instance of `Hensel::Builder::Item`.
256
+
257
+ However, if you use `render` with block, a few configuration(`richsnippets`, `last_item_link`) will be ignored.
258
+
259
+ ```ruby
260
+ builder = Hensel::Builder.new
261
+ builder.add(text: "home", url: "/")
262
+
263
+ builder.render {|item| "<li>#{item.text}</li>" } #=> "<ul><li>home</li></ul>"
264
+ builder.render do
265
+ if last?
266
+ node(:li) do
267
+ node(:span){ item.text }
268
+ end
269
+ else
270
+ node(:li) do
271
+ node(:a, href: item.url) do
272
+ node(:span){ item.text }
273
+ end
274
+ end
275
+ end
276
+ end
277
+ ```
278
+
279
+ ### Helpers
280
+
281
+ The helper is prepared for use in Sinatra and Padrino.
282
+
283
+ ### with Sinatra
284
+
285
+ ```ruby
286
+ class Sample < Sinatra::Base
287
+ helpers Hensel::Helpers
288
+
289
+ configure do
290
+ Hensel.configure do |config|
291
+ config.attr_wrapper = '"'
292
+ config.whitespace = ' '
293
+ end
294
+ end
295
+
296
+ get "/" do
297
+ breadcrumbs.add("home", "/")
298
+ breadcrumbs.render
299
+ end
300
+ end
301
+ ```
302
+
303
+ ### with Padrino
304
+
305
+ ```ruby
306
+ # config/boot.rb
307
+ Padrino.before_load do
308
+ Hensel.configure do |config|
309
+ config.attr_wrapper = '"'
310
+ config.whitespace = ' '
311
+ end
312
+ end
313
+ ```
314
+
315
+ ```ruby
316
+ class Sample < Padrino::Application
317
+ helpers Hensel::Helpers
318
+
319
+ get :index do
320
+ breadcrumbs.add("home", ?/)
321
+ breadcrumbs.render
322
+ end
323
+ end
324
+ ```
325
+
326
+ ## TODO
327
+
328
+ * Support Rails
329
+ * New syntax for Sinatra and Padrino
330
+
331
+ ## Contributing
332
+
333
+ 1. Fork it ( https://github.com/namusyaka/hensel/fork )
334
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
335
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
336
+ 4. Push to the branch (`git push origin my-new-feature`)
337
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ desc "Run all specs."
5
+ RSpec::Core::RakeTask.new(:spec) do |spec|
6
+ spec.pattern = 'spec/*_spec.rb'
7
+ spec.rspec_opts = %w(--format NyanCatFormatter)
8
+ end
9
+ task default: :spec
data/hensel.gemspec ADDED
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'hensel/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "hensel"
8
+ spec.version = Hensel::VERSION
9
+ spec.authors = ["namusyaka"]
10
+ spec.email = ["namusyaka@gmail.com"]
11
+ spec.summary = %q{Hensel makes it easy to build the breadcrumbs.}
12
+ spec.description = spec.summary
13
+ spec.homepage = "https://github.com/namusyaka/hensel"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.6"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rspec"
24
+ spec.add_development_dependency "nyan-cat-formatter"
25
+ spec.add_development_dependency "rspec-html-matchers"
26
+ spec.add_development_dependency "guard"
27
+ spec.add_development_dependency "guard-shell"
28
+ spec.add_development_dependency "guard-rspec"
29
+ end
@@ -0,0 +1,43 @@
1
+ require 'forwardable'
2
+ require 'hensel/builder/node'
3
+
4
+ module Hensel
5
+ class Builder
6
+ class Item < Node
7
+ extend Forwardable
8
+
9
+ attr_accessor :text, :url, :options
10
+ attr_accessor :_first, :_last, :renderer
11
+
12
+ def_delegators :@options, :[], :[]=
13
+
14
+ def initialize(text, url, **options)
15
+ @text = h(text)
16
+ @url = url
17
+ @options = options
18
+ end
19
+
20
+ def render
21
+ if renderer
22
+ instance_eval(&renderer)
23
+ else
24
+ node(:li, **options) do
25
+ if !Hensel.configuration.last_item_link? && item.last?
26
+ node(:span){ item.text }
27
+ else
28
+ node(:a, href: item.url){ item.text }
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ def first?
35
+ !!_first
36
+ end
37
+
38
+ def last?
39
+ !!_last
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,53 @@
1
+ require 'hensel/helpers/tag_helpers'
2
+
3
+ module Hensel
4
+ class Builder
5
+ class Node < Struct.new(:name, :attributes, :content, :parent, :indent)
6
+ include Hensel::Helpers::TagHelpers
7
+
8
+ def node(name, content = nil, **attributes, &block)
9
+ element = Node.new(name, attributes, content, self, child_indent)
10
+ content =
11
+ if block_given?
12
+ result = element.instance_eval(&block)
13
+ element.children.length.zero? ? result : element.render
14
+ else
15
+ content
16
+ end
17
+ children << (child = content_tag(name, content, attributes.merge!(indent: child_indent), &block))
18
+ child
19
+ end
20
+
21
+ def render
22
+ children.join(Hensel.configuration.indentation? ? "\n" : "")
23
+ end
24
+
25
+ def children
26
+ @children ||= []
27
+ end
28
+
29
+ def item
30
+ @item ||=
31
+ begin
32
+ return self if self.instance_of?(Hensel::Builder::Item)
33
+ ancestor = parent
34
+ while ancestor.instance_of?(Hensel::Builder::Node)
35
+ ancestor = ancestor.parent
36
+ end
37
+ ancestor
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ def child_indent
44
+ @child_indent ||=
45
+ if Hensel.configuration.indentation?
46
+ indent ? indent + 1 : 1
47
+ else
48
+ 0
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,105 @@
1
+ require 'hensel/builder/item'
2
+ require 'hensel/helpers/tag_helpers'
3
+ require 'hensel/filters'
4
+
5
+ module Hensel
6
+ class Builder
7
+ include Helpers::TagHelpers
8
+
9
+ attr_reader :items, :options
10
+
11
+ def initialize(**options)
12
+ @items = []
13
+ @options = options
14
+ end
15
+
16
+ # Adds an item to items
17
+ def add(*arguments)
18
+ items << (item = Item.new(*parse_arguments(arguments)))
19
+ item.parent = self
20
+ item
21
+ end
22
+
23
+ # Removes the item from items
24
+ # @example without block
25
+ # builder = Hensel::Builder.new
26
+ # builder.add("Index", "/")
27
+ # builder.remove("Index")
28
+ #
29
+ # @example with block
30
+ # builder = Hensel::Builder.new
31
+ # builder.add("Index", "/")
32
+ # builder.remove{|item| item.text == "Index" }
33
+ def remove(text = nil, &block)
34
+ block_given? ? items.delete_if(&block) : items.delete_if{|item| text == item.text }
35
+ end
36
+
37
+ # Renders the breadcrumb html
38
+ def render(&block)
39
+ process! unless processed?
40
+
41
+ concatenated_items = map_items do |item|
42
+ if block_given?
43
+ block.arity.zero? ? item.instance_eval(&block) : block.call(item)
44
+ else
45
+ item.render
46
+ end
47
+ end.join(Hensel.configuration.indentation? ? "\n" : "")
48
+
49
+ content_tag(Hensel.configuration.parent_element, concatenated_items, options)
50
+ end
51
+
52
+ def processed?
53
+ !!@processed
54
+ end
55
+
56
+ private
57
+
58
+ def map_items
59
+ items_length = items.length.pred
60
+ items.map.with_index do |item, index|
61
+ if index == items_length
62
+ item._last = true
63
+ elsif index.zero?
64
+ item._first = true
65
+ else
66
+ item._first = item._last = false
67
+ end
68
+ item_filters.each{|filter| item.instance_eval(&filter) } unless item_filters.empty?
69
+ yield item
70
+ end
71
+ end
72
+
73
+ def item_filters
74
+ @item_filters ||= []
75
+ end
76
+
77
+ # @!visibility private
78
+ def process!
79
+ configuration = Hensel.configuration
80
+
81
+ Hensel::Filters.filters[:filters].each_pair do |name, block|
82
+ instance_eval(&block) if configuration.send(:"#{name}?")
83
+ end
84
+ richsnippet_filter = Hensel::Filters.filters[:richsnippets][configuration.richsnippet]
85
+ item_filters << richsnippet_filter if richsnippet_filter
86
+ @processed = true
87
+ end
88
+
89
+ # @!visibility private
90
+ def parse_arguments(arguments)
91
+ case arguments.length
92
+ when 3
93
+ arguments
94
+ when 2
95
+ arguments << {}
96
+ when 1
97
+ if (options = arguments.first) && options.instance_of?(Hash)
98
+ [ options.delete(:text) || options.delete(:content), options.delete(:path) || options.delete(:url), options ]
99
+ else
100
+ raise ArgumentError
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end