nanoc-toolbox 0.0.2 → 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.
data/.gitignore CHANGED
@@ -7,3 +7,4 @@ pkg/*
7
7
  .yardoc
8
8
  rdoc
9
9
  coverage
10
+ doc
data/CHANGELOG.md CHANGED
@@ -2,11 +2,20 @@
2
2
 
3
3
  ## developement
4
4
 
5
+ ## Release 0.0.3
6
+
7
+ * NEW: Gravatar Helper
8
+ * CHANGED: Better Yardoc documentation
9
+ * NEW: RSpec tests
10
+ * NEW: HTML Tag Helper
11
+ * NEW: HtlmTidy Filter
12
+
13
+ ## Release 0.0.2
14
+
5
15
  * NEW: README, LICENSE and CHANGELOG files
6
16
  * FIXED: Correct the gemspec hompage, and removed rubyforge reference
7
17
  * CHANGED: Cleanup on the navigation helper
8
18
  * NEW: Add Yardoc
9
- * NEW: RSPEC Test framework
10
19
 
11
20
  ## Release 0.0.1
12
21
 
data/Gemfile.lock CHANGED
@@ -1,17 +1,28 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- nanoc-toolbox (0.0.1)
4
+ nanoc-toolbox (0.0.3)
5
5
  nanoc (>= 3.1.6)
6
+ nokogiri (>= 1.4.4)
6
7
 
7
8
  GEM
8
9
  remote: http://rubygems.org/
9
10
  specs:
10
11
  cri (1.0.1)
12
+ diff-lcs (1.1.2)
11
13
  nanoc (3.1.6)
12
14
  nanoc3 (>= 3.1.6)
13
15
  nanoc3 (3.1.6)
14
16
  cri (>= 1.0.0)
17
+ nokogiri (1.4.4)
18
+ rspec (2.4.0)
19
+ rspec-core (~> 2.4.0)
20
+ rspec-expectations (~> 2.4.0)
21
+ rspec-mocks (~> 2.4.0)
22
+ rspec-core (2.4.0)
23
+ rspec-expectations (2.4.0)
24
+ diff-lcs (~> 1.1.2)
25
+ rspec-mocks (2.4.0)
15
26
 
16
27
  PLATFORMS
17
28
  ruby
@@ -20,3 +31,5 @@ DEPENDENCIES
20
31
  bundler (>= 1.0.0)
21
32
  nanoc (>= 3.1.6)
22
33
  nanoc-toolbox!
34
+ nokogiri (>= 1.4.4)
35
+ rspec (>= 1.0.0)
data/README.md CHANGED
@@ -1,19 +1,23 @@
1
1
  # nanoc-toolbox
2
2
 
3
+ !!! THIS LIBRARY IS STILL UNDER DEVELOPMENT !!!
4
+
3
5
  ## Presentation
4
6
 
5
- The nanoc-toolbox is a collection of filters and helpers for the static site generator tool nanoc
7
+ The nanoc-toolbox is a collection of filters and helpers for the static site generator tool nanoc.
6
8
 
7
9
  ## Features
8
10
 
9
11
  * Navigation Helper
12
+ * Gravatar Helper
13
+ * HtmlTag Helper
10
14
 
11
15
  ## Requirements
12
16
 
13
17
  * nanoc3
18
+ * Nokogiri
14
19
 
15
-
16
- ## Instalation
20
+ ## Installation
17
21
 
18
22
  To use the nanoc-toolbox, you have to start by installing the gem.
19
23
 
@@ -34,6 +38,7 @@ The following example shows a sample helpers_.rb file in the lib directory
34
38
 
35
39
  # Custom Helpers
36
40
  include Nanoc::Toolbox::Helpers::Navigation
41
+ include Nanoc::Toolbox::Helpers::Gravatar
37
42
 
38
43
  ## Acknowledgments
39
44
 
@@ -0,0 +1,21 @@
1
+ module Nanoc::Toolbox::Filters
2
+ # NANOC Filter for html output tidy
3
+ #
4
+ # @author Anouar ADLANI <anouar@adlani.com>
5
+ class HtmlTidy < Nanoc3::Filter
6
+
7
+ identifier :html_tidy
8
+
9
+ # Tidy the HTML output
10
+ # Runs the content through Nokogiry document parser, and request the html output,
11
+ # which is tidied by default.
12
+ #
13
+ # @param [String] content The content to filter
14
+ # @param [String] params This method takes no options.
15
+ # @return [String] The filtered content
16
+ def run(content, params = {})
17
+ require 'nokogiri'
18
+ Nokogiri::HTML::Document.parse(content).to_html
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,6 @@
1
+ require 'nanoc/toolbox/filters/html_tidy'
2
+
3
+ # This module will regroup all the filters for nanoc
4
+ module Nanoc::Toolbox::Filters
5
+
6
+ end
@@ -0,0 +1,143 @@
1
+ require 'nanoc/toolbox/helpers/html_tag'
2
+
3
+ module Nanoc::Toolbox::Helpers
4
+
5
+ # NANOC Helper for the Gravatar avatars
6
+ #
7
+ # This module contains functions for generating URL or IMG tags to
8
+ # display the Gravatar linked to an address email, or the default gravatar
9
+ #
10
+ # @see http://en.gravatar.com/
11
+ #
12
+ # @author Anouar ADLANI <anouar@adlani.com>
13
+ module Gravatar
14
+ include Nanoc::Toolbox::Helpers::HtmlTag
15
+
16
+ # Gravatar avatar image base URL
17
+ URL = { :http => "http://gravatar.com/avatar/",
18
+ :https => "https://secure.gratatar.com/avatar/" }
19
+
20
+ # Collection of allowed ratings
21
+ RATING = %w{g pg r x}
22
+
23
+ # Default Icon sets
24
+ ICONS = %w{none identicon monsterid wavatar 404}
25
+
26
+ # Size range available
27
+ SIZE = 1..512
28
+
29
+ # Available options that could be configure
30
+ HELPER_OPTION = [:ext, :secure]
31
+ GRAVATAR_OPTIONS = [:default_icon, :size, :rating]
32
+ AVAILABLE_OPTIONS = HELPER_OPTION + GRAVATAR_OPTIONS
33
+
34
+ # Default values set to the options
35
+ DEFAULT_VALUES = { :size => nil, :rating => nil, :ext => false, :secure => false }
36
+
37
+ # Generate the image tag to the gravatar of the supplied email
38
+ #
39
+ # @example
40
+ # gravatar_image('anouar@adlani.com', :size => "100")
41
+ # #=> <img src="http://gravatar.com/avatar/4d076af1db60b16e1ce080505baf821c?size=100" />
42
+ #
43
+ # @param [String] email - the email address of the gravatar account
44
+ # @param options (see #build_options_parameters)
45
+ # @option options (see #build_options_parameters)
46
+ def gravatar_image(email, options={})
47
+ image_url = gravatar_url(email, options)
48
+ options.delete_if {|key, value| GRAVATAR_OPTIONS.include?(key) }
49
+ tag('img', options.merge(:src => image_url))
50
+ end
51
+
52
+
53
+ # Generate image URL
54
+ #
55
+ # @example
56
+ # gravatar_url('anouar@adlani.com', :size => "512", :ext => true)
57
+ # #=> "http://gravatar.com/avatar/4d076af1db60b16e1ce080505baf821c.jpg?size=512"
58
+ #
59
+ # @param email (see #gravatar_image)
60
+ # @param options (see #build_options_parameters)
61
+ # @option options (see #build_options_parameters)
62
+ def gravatar_url(email, options={})
63
+ # Prepare the email
64
+ email = clean_email(email)
65
+
66
+ # Prepare the options
67
+ options = DEFAULT_VALUES.merge(options)
68
+ options = clean_options(options)
69
+
70
+ # Retreive the URL depending on the protocole and build it
71
+ protocole = options[:secure] ? :https : :http
72
+ host = URL[protocole]
73
+
74
+ # generate the image name and append a the extension if requested
75
+ file = hash_email(email)
76
+ file += '.jpg' if options[:ext]
77
+
78
+ # Build the query string
79
+ parameters = build_options_parameters(options)
80
+
81
+ "#{host}#{file}#{parameters}"
82
+ end
83
+
84
+ protected
85
+ # Filters, Validates and build the options parameters for the URL.
86
+ # It simply consists in returning the URL query string based on the options passed
87
+ # after validating the availability the correctness of the option
88
+ #
89
+ # @example
90
+ # {:size => 225, :rating => 'g', :ext => false, :secure => false}
91
+ # #=> "?size=225&rating=g"
92
+ #
93
+ # @param [Hash] options the options to pass to the gravatar URL
94
+ # @option options [String] default_icon
95
+ # @option options [String] size (nil) Size of the image, between 1 to 512
96
+ # @option options [String] rating (nil) The rating allowed, a value in g, pg, r or x
97
+ # @option options [Boolean] ext (false) Use or not a file extension
98
+ # @option options [Boolean] secure (false) Use or not https
99
+ def build_options_parameters(options={})
100
+ # Remove unecessary options
101
+ options.delete_if {|key, value| !GRAVATAR_OPTIONS.include?(key) }
102
+
103
+ # Return now if the options hash is empty after cleanup
104
+ return '' if options.empty?
105
+
106
+ # Build the parameters string
107
+ '?' + options.sort.map { |e| e = e.join('=') if e.size == 2}.join('&')
108
+ end
109
+
110
+ private
111
+ # Generate email address hash
112
+ def hash_email(email)
113
+ require 'digest/md5'
114
+ Digest::MD5.hexdigest(email)
115
+ end
116
+
117
+ # @example
118
+ # {:size => 225, :rating => 'g', :ext => false, :secure => false}
119
+ # #=> "?size=225&rating=g"
120
+ def clean_options(options={})
121
+ # remove unsupported options
122
+ options.delete_if {|key, value| !AVAILABLE_OPTIONS.include?(key) }
123
+
124
+ # remove empty options
125
+ options.delete_if {|key, value| value.nil? || value.to_s.strip == '' }
126
+
127
+ # remove options with unsupported values
128
+ options.delete(:default_icon) unless ICONS.include?(options[:default_icon])
129
+ options.delete(:rating) unless RATING.include?(options[:rating])
130
+ options.delete(:size) unless SIZE.include?(options[:size].to_i)
131
+
132
+ # cleanned up options
133
+ options
134
+ end
135
+
136
+ # Clean up and validates email
137
+ def clean_email(email='')
138
+ raise ArgumentError.new('Invalid email') if email.empty? || email.index('@').nil? || email.index('@') == 0 || email.count('@') > 1 || email.size <= 6
139
+ email.strip
140
+ end
141
+
142
+ end
143
+ end
@@ -0,0 +1,47 @@
1
+ module Nanoc::Toolbox::Helpers
2
+
3
+ # NANOC Helper for HTML Tags
4
+ #
5
+ # This module contains functions for generating simple tags with attribute
6
+ #
7
+ # @author Anouar ADLANI <anouar@adlani.com>
8
+ module HtmlTag
9
+ # Simple tag
10
+ #
11
+ # @example
12
+ # tag("br")
13
+ # # => <br />
14
+ #
15
+ # tag("hr", class => "thin", true)
16
+ # # => <br class="thin">
17
+ #
18
+ # tag("input", :type => 'text')
19
+ # # => <input type="text" />
20
+ #
21
+ def tag(name, options={}, open=false)
22
+ "<#{name}#{tag_options(options) if options}#{open ? ">" : " />"}"
23
+ end
24
+
25
+ # Content tag
26
+ #
27
+ # @example
28
+ # content_tag(:p, "Hello world!")
29
+ # # => <p>Hello world!</p>
30
+ # content_tag(:div, content_tag(:p, "Hello world!"), :class => "strong")
31
+ # # => <div class="strong"><p>Hello world!</p></div>
32
+ def content_tag(name, content, options={})
33
+ "<#{name}#{tag_options(options) if options}>#{content}</#{name}>"
34
+ end
35
+
36
+ protected
37
+ def tag_options(options)
38
+ unless options.empty?
39
+ attributes = []
40
+ options.each do |key, value|
41
+ attributes << %(#{key}="#{value}")
42
+ end
43
+ ' ' + attributes.join(' ')
44
+ end
45
+ end
46
+ end
47
+ end
@@ -1,101 +1,102 @@
1
- module Nanoc::Toolbox::Helpers
1
+ require 'nanoc/toolbox/helpers/html_tag'
2
2
 
3
+ module Nanoc::Toolbox::Helpers
3
4
  # NANOC Helper for the Navigation related stuff.
5
+ #
4
6
  # This module contains functions for generating navigation menus for your
5
7
  # pages, like navigation menu, breadcrumbs or a table of content for a given Item
6
8
  #
7
9
  # @author Anouar ADLANI <anouar@adlani.com>
8
10
  module Navigation
11
+ include Nanoc3::Helpers::LinkTo
12
+ include Nanoc::Toolbox::Helpers::HtmlTag
9
13
 
10
- # Generate a navigation menu for a given item.
11
- # The menu will be generated form the identifier of the desired root element.
12
- # The root itself will not be rendered. It generate the menu by parsing all
14
+ # Generate a navigation menu for a given item.
15
+ # The menu will be generated form the identifier of the desired root element.
16
+ # The root itself will not be rendered. It generate the menu by parsing all
13
17
  # the descendent of the passed item.
14
- #
18
+ #
15
19
  # @param [String] identifier - the identifier string of the root element
16
- # @param [Hash] params - The Optional parameters
17
- # @option params [Interger] :depth (3) maximum depth of the rendered menu
18
- # @option params [String] :collection_tag ('ol') collection englobing tag name
19
- # @option params [String] :item_tag ('li') item englobing tag name
20
- #
20
+ # @param [Hash] options - The Optional parameters
21
+ # @option options [Interger] :depth (3) maximum depth of the rendered menu
22
+ # @option options [String] :collection_tag ('ol') collection englobing tag name
23
+ # @option options [String] :item_tag ('li') item englobing tag name
24
+ #
21
25
  # @return [String] The output ready to be displayed by the caller
22
- def navigation_for(identifier, params={})
23
- # Parse params or set to default values
24
- params[:depth] ||= 3
25
- params[:collection_tag] ||= 'ol'
26
- params[:item_tag] ||= 'li'
27
-
28
- # Decrease the depth level
29
- params[:depth] -= 1
30
-
26
+ def navigation_for(identifier, options={})
27
+ # Parse options or set to default values
28
+ options[:depth] ||= 3
29
+ options[:collection_tag] ||= 'ol'
30
+ options[:item_tag] ||= 'li'
31
+
31
32
  # Get root item for which we need to draw the navigation
32
33
  root = @items.find { |i| i.identifier == identifier }
33
-
34
+
34
35
  # Do not render if there is no child
35
36
  return nil unless root.children
36
-
37
+
37
38
  # Find all sections, and render them
38
39
  sections = find_item_tree(root)
39
- render_menu(sections, params)
40
+ render_menu(sections, options)
40
41
  end
41
-
42
-
42
+
43
+
43
44
  # Generate a Table of Content for a given item. The toc will be generated
44
45
  # form the item content. The parsing is done with Nokogiri through XPath.
45
- #
46
+ #
46
47
  # @param [String] item_rep - the representation of desired item
47
- # @param [Hash] params - The Optional parameters
48
- # @option params [Interger] :depth (3) maximum depth of the rendered menu
49
- # @option params [String] :collection_tag ('ol') collection englobing tag name
50
- # @option params [String] :item_tag ('li') item englobing tag name
51
- #
48
+ # @param [Hash] options - The Optional parameters
49
+ # @option options [Interger] :depth (3) maximum depth of the rendered menu
50
+ # @option options [String] :collection_tag ('ol') collection englobing tag name
51
+ # @option options [String] :item_tag ('li') item englobing tag name
52
+ #
52
53
  # @return [String] The output ready to be displayed by the caller
53
54
  #
54
55
  # @see http://nokogiri.org/
55
- def toc_for(item_rep, params={})
56
+ def toc_for(item_rep, options={})
56
57
  require 'nokogiri'
57
-
58
- # Parse params or set to default values
59
- params[:depth] ||= 3
60
- params[:collection_tag] ||= 'ol'
61
- params[:item_tag] ||= 'li'
62
- params[:path] ||= 'div[@class="section"]'
63
-
58
+
59
+ # Parse options or set to default values
60
+ options[:depth] ||= 3
61
+ options[:collection_tag] ||= 'ol'
62
+ options[:item_tag] ||= 'li'
63
+ options[:path] ||= 'div[@class="section"]'
64
+
64
65
  # Retreive the parsed content and init nokogiri
65
66
  compiled_content = item_rep.instance_eval { @content[:pre] }
66
67
  doc = Nokogiri::HTML(compiled_content)
67
68
  doc_root = doc.xpath('/html/body').first
68
-
69
+
69
70
  # Find all sections, and render them
70
- sections = find_toc_sections(doc_root, "#{params[:path]}", 2)
71
- render_menu(sections, params)
71
+ sections = find_toc_sections(doc_root, options[:path])
72
+ render_menu(sections, options)
72
73
  end
73
-
74
-
75
- # Generate a Breadcrumb for a given item. The breadcrumbs, is starting with
76
- # the root item and ending with the item itself.
74
+
75
+
76
+ # Generate a Breadcrumb for a given item. The breadcrumbs, is starting with
77
+ # the root item and ending with the item itself.
77
78
  #
78
79
  # Requires the Helper: Nanoc3::Helpers::Breadcrumbs
79
- #
80
+ #
80
81
  # @param [String] identifier - the identifier string of element
81
- # @param [Hash] params - The Optional parameters
82
- # @option params [String] :collection_tag ('ol') collection englobing tag name
83
- # @option params [String] :item_tag ('li') item englobing tag name
84
- #
82
+ # @param [Hash] options - The Optional parameters
83
+ # @option options [String] :collection_tag ('ol') collection englobing tag name
84
+ # @option options [String] :item_tag ('li') item englobing tag name
85
+ #
85
86
  # @return [String] The output ready to be displayed by the caller
86
87
  #
87
88
  # @see Nanoc3::Helpers::Breadcrumbs#breadcrumbs_for_identifier
88
- def breadcrumb_for(identifier, params={})
89
-
90
- # Parse params or set to default values
91
- params[:collection_tag] ||= 'ul'
92
- params[:item_tag] ||= 'li'
93
-
89
+ def breadcrumb_for(identifier, options={})
90
+
91
+ # Parse options or set to default values
92
+ options[:collection_tag] ||= 'ul'
93
+ options[:item_tag] ||= 'li'
94
+
94
95
  # Retreive the breadcrumbs trail and format them
95
96
  sections = find_breadcrumbs_trail(identifier)
96
- render_menu(sections, params)
97
+ render_menu(sections, options)
97
98
  end
98
-
99
+
99
100
  # Render a Hash to a HTML List by default
100
101
  #
101
102
  # Hash structure should be construct like this:
@@ -120,86 +121,81 @@ module Nanoc::Toolbox::Helpers
120
121
  # </ul>
121
122
  #
122
123
  # @param [Array] items - The array of links that need to be rendered
123
- # @param [Hash] params - The Optional parameters
124
- # @option params [String] :collection_tag ('ol') collection englobing tag name
125
- # @option params [String] :item_tag ('li') item englobing tag name
126
- #
124
+ # @param [Hash] options - The Optional parameters
125
+ # @option options [String] :collection_tag ('ol') collection englobing tag name
126
+ # @option options [String] :item_tag ('li') item englobing tag name
127
+ #
127
128
  # @return [String] The output ready to be displayed by the caller
128
- def render_menu(items, params={})
129
+ def render_menu(items, options={})
130
+
131
+ # Parse options or set to default values
132
+ options[:depth] ||= 3
133
+ options[:collection_tag] ||= 'ol'
134
+ options[:item_tag] ||= 'li'
129
135
 
130
- # Parse params or set to default values
131
- params[:depth] ||= 3
132
- params[:collection_tag] ||= 'ol'
133
- params[:item_tag] ||= 'li'
134
-
135
136
  # Decrease the depth level
136
- params[:depth] -= 1
137
-
137
+ options[:depth] -= 1
138
+
138
139
  rendered_menu = items.map do |item|
139
-
140
+
140
141
  # Render only if there is depth left
141
- if params[:depth].to_i >= 0 && item[:subsections]
142
- output = render_menu(item[:subsections], params)
143
- params[:depth] += 1 # Increase the depth level after the call of navigation_for
142
+ if options[:depth].to_i > 0 && item[:subsections]
143
+ output = render_menu(item[:subsections], options)
144
+ options[:depth] += 1 # Increase the depth level after the call of navigation_for
144
145
  end
145
146
  output ||= ""
146
- content_tag(params[:item_tag], link_to(item[:title], item[:link]) + output)
147
-
147
+ content_tag(options[:item_tag], link_to(item[:title], item[:link]) + output)
148
+
148
149
  end.join()
149
-
150
- content_tag(params[:collection_tag], rendered_menu)
150
+
151
+ content_tag(options[:collection_tag], rendered_menu) unless rendered_menu.empty?
151
152
  end
152
-
153
+
153
154
  private
154
155
 
155
- # Really basic Helper Method that wrap a content within an HTML Tag
156
- def content_tag(name, content="", &block)
157
- "<#{name}>#{content}</#{name}>"
158
- end
159
-
160
- # Recursive method that extract from an XPath pattern the document structure
161
- # and return the "permalinks" to each sections in an Array of Hash that
162
- # could be used by the rendering method. The structure is deducted by the
156
+ # Recursive method that extract from an XPath pattern the document structure
157
+ # and return the "permalinks" to each sections in an Array of Hash that
158
+ # could be used by the rendering method. The structure is deducted by the
163
159
  # H1-6 header within the html element defined by the XPATH
164
160
  def find_toc_sections(section, section_xpath, title_level=1)
165
161
  return {} unless section.xpath(section_xpath)
166
-
167
- # For each section found call the find_toc_sections on it with an
162
+
163
+ # For each section found call the find_toc_sections on it with an
168
164
  # increased header level (ex: h1 => h2) and then generate the hash res
169
165
  sections = section.xpath(section_xpath).map do |subsection|
170
166
  header = subsection.xpath("h#{title_level}").first
171
167
  sub_id = subsection['id']
172
168
  sub_title = header.inner_html if header
173
169
  subsections = {}
174
-
175
-
170
+
171
+
176
172
  if subsection.xpath("#{section_xpath}") && title_level <= 6
177
173
  subsections = find_toc_sections(subsection, "#{section_xpath}", title_level+1)
178
174
  end
179
175
  { :title => sub_title, :link => '#' + sub_id, :subsections => subsections }
180
176
  end
181
177
  end
182
-
183
- # Recursive method that extract from an XPath pattern the document structure
184
- # and return the "permalinks" in a Array of Hash that could be used by the
178
+
179
+ # Recursive method that extract from an XPath pattern the document structure
180
+ # and return the "permalinks" in a Array of Hash that could be used by the
185
181
  # rendering method
186
182
  def find_item_tree(root)
187
183
  return nil unless root.children
188
-
184
+
189
185
  # For each child call the find_item_tree on it and then generate the hash
190
186
  sections = root.children.map do |child|
191
- subsections = find_item_tree(child)
187
+ subsections = find_item_tree(child)
192
188
 
193
- { :title => (child[:title] || child.identifier),
189
+ { :title => (child[:title] || child.identifier),
194
190
  :link => relative_path_to(child),
195
191
  :subsections => subsections }
196
192
  end
197
193
  end
198
-
199
-
200
- def find_breadcrumbs_trail(root)
194
+
195
+
196
+ def find_breadcrumbs_trail(root)
201
197
  sections = breadcrumbs_for_identifier(root).map do |child|
202
- { :title => (child[:title] || child.identifier),
198
+ { :title => (child[:title] || child.identifier),
203
199
  :link => relative_path_to(child),
204
200
  :subsections => nil }
205
201
  end