nanoc-toolbox 0.0.2 → 0.0.3

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