hensel 0.0.1

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