hx 0.7.4 → 0.8.2

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/VERSION CHANGED
@@ -1 +1 @@
1
- 0.7.4
1
+ 0.8.2
data/bin/hx CHANGED
@@ -1,3 +1,4 @@
1
1
  #!/usr/bin/env ruby
2
- require 'hx/commandline'
3
- Hx::Commandline.main(*ARGV)
2
+ require 'rubygems'
3
+ require 'hx/cli'
4
+ Hx::CLI.main(*ARGV)
data/lib/hx.rb CHANGED
@@ -21,12 +21,12 @@
21
21
  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
22
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
23
 
24
- require 'rubygems'
25
24
  require 'thread'
26
25
  require 'set'
27
26
  require 'pathname'
28
27
  require 'tempfile'
29
28
  require 'yaml'
29
+ require 'hx/path'
30
30
 
31
31
  module Hx
32
32
 
@@ -38,17 +38,18 @@ end
38
38
  class EditingNotSupportedError < RuntimeError
39
39
  end
40
40
 
41
+ # minimal complete definition: each_entry_path + get_entry, or each_entry
41
42
  module Filter
42
43
  def edit_entry(path, prototype=nil)
43
44
  raise EditingNotSupportedError, "Editing not supported for #{path}"
44
45
  end
45
46
 
46
- def each_entry_path
47
- each_entry { |path, entry| yield path }
47
+ def each_entry_path(selector)
48
+ each_entry(selector) { |path, entry| yield path }
48
49
  end
49
50
 
50
- def each_entry
51
- each_entry_path do |path|
51
+ def each_entry(selector)
52
+ each_entry_path(selector) do |path|
52
53
  begin
53
54
  entry = get_entry(path)
54
55
  rescue NoSuchEntryError
@@ -59,8 +60,8 @@ module Filter
59
60
  end
60
61
 
61
62
  def get_entry(path)
62
- each_entry do |entry_path, entry|
63
- return entry if entry_path == path
63
+ each_entry(Path.literal(path)) do |entry_path, entry|
64
+ return entry
64
65
  end
65
66
  raise NoSuchEntryError, path
66
67
  end
@@ -69,7 +70,7 @@ end
69
70
  class NullInput
70
71
  include Filter
71
72
 
72
- def each_entry
73
+ def each_entry(selector)
73
74
  self
74
75
  end
75
76
  end
@@ -79,13 +80,25 @@ NULL_INPUT = NullInput.new
79
80
  class PathSubset
80
81
  include Filter
81
82
 
83
+ def patterns_to_selector(patterns)
84
+ patterns.map { |p| Path::parse_pattern(p) }.inject { |a, b| a | b }
85
+ end
86
+ private :patterns_to_selector
87
+
82
88
  def initialize(input, options)
83
89
  @input = input
84
- @path_filter = Predicate.new(options[:only], options[:except])
90
+ only = patterns_to_selector(Array(options[:only] || []))
91
+ except = patterns_to_selector(Array(options[:except] || []))
92
+ except = ~except if except
93
+ if only and except
94
+ @selector = only & except
95
+ else
96
+ @selector = only || except || Path::ALL
97
+ end
85
98
  end
86
99
 
87
100
  def edit_entry(path, prototype=nil)
88
- if @path_filter.accept? path
101
+ if @selector.accept? path
89
102
  @input.edit_entry(path, prototype) { |text| yield text }
90
103
  else
91
104
  raise EditingNotSupportedError, "Editing not supported for #{path}"
@@ -93,47 +106,20 @@ class PathSubset
93
106
  self
94
107
  end
95
108
 
96
- def each_entry_path
97
- @input.each_entry_path do |path|
98
- yield path if @path_filter.accept? path
99
- end
109
+ def each_entry_path(selector, &block)
110
+ @input.each_entry_path(@selector & selector, &block)
100
111
  self
101
112
  end
102
113
 
103
- def get_entry(path)
104
- raise NoSuchEntryError, path unless @path_filter.accept? path
105
- @input.get_entry(path)
106
- end
107
- end
108
-
109
- class PathSubset::Predicate
110
- def initialize(accept, reject)
111
- @accept_re = patterns_to_re(accept)
112
- @reject_re = patterns_to_re(reject)
113
- end
114
-
115
- def accept?(path)
116
- (not @accept_re or path =~ @accept_re) and
117
- (not @reject_re or path !~ @reject_re)
118
- end
119
-
120
- def patterns_to_re(patterns)
121
- return nil if patterns.nil? or patterns.empty?
122
- patterns = Array(patterns)
123
- Regexp.new("(?:#{patterns.map { |p| pattern_to_re(p) }.join("|")})")
114
+ def each_entry(selector, &block)
115
+ @input.each_entry(@selector & selector, &block)
116
+ self
124
117
  end
125
- private :patterns_to_re
126
118
 
127
- def pattern_to_re(pattern)
128
- "^#{pattern.scan(/(\*\*?|[^*]+)/).map { |s,|
129
- case s
130
- when "**"; ".*"
131
- when "*"; "[^/]*"
132
- else Regexp.quote(s)
133
- end
134
- }}$"
119
+ def get_entry(path)
120
+ raise NoSuchEntryError, path unless @selector.accept? path
121
+ @input.get_entry(path)
135
122
  end
136
- private :pattern_to_re
137
123
  end
138
124
 
139
125
  class Overlay
@@ -143,10 +129,10 @@ class Overlay
143
129
  @inputs = inputs
144
130
  end
145
131
 
146
- def each_entry_path
132
+ def each_entry_path(selector)
147
133
  seen = Set[]
148
134
  @inputs.each do |input|
149
- input.each_entry_path do |path|
135
+ input.each_entry_path(selector) do |path|
150
136
  yield path unless seen.include? path
151
137
  seen.add path
152
138
  end
@@ -197,8 +183,11 @@ class AddPath
197
183
  self
198
184
  end
199
185
 
200
- def each_entry_path
201
- @input.each_entry_path { |path| yield add_circumfix(path) }
186
+ def each_entry_path(selector)
187
+ @input.each_entry_path(Path::ALL) do |path|
188
+ path = add_circumfix(path)
189
+ yield path if selector.accept? path
190
+ end
202
191
  self
203
192
  end
204
193
 
@@ -218,10 +207,10 @@ class StripPath
218
207
  self
219
208
  end
220
209
 
221
- def each_entry_path
222
- @input.each_entry_path do |path|
210
+ def each_entry_path(selector)
211
+ @input.each_entry_path(Path::ALL) do |path|
223
212
  path = strip_circumfix(path)
224
- yield path if path
213
+ yield path if path and selector.accept? path
225
214
  end
226
215
  self
227
216
  end
@@ -246,14 +235,14 @@ class Cache
246
235
  self
247
236
  end
248
237
 
249
- def each_entry
238
+ def each_entry(selector)
250
239
  entries = nil
251
240
  @lock.synchronize do
252
241
  if @entries
253
242
  entries = @entries
254
243
  else
255
244
  entries = []
256
- @input.each_entry do |path, entry|
245
+ @input.each_entry(Path::ALL) do |path, entry|
257
246
  @entries_by_path[path] = entry
258
247
  entries << [path, entry]
259
248
  end
@@ -261,7 +250,7 @@ class Cache
261
250
  end
262
251
  end
263
252
  entries.each do |path, entry|
264
- yield path, entry.dup
253
+ yield path, entry.dup if selector.accept? path
265
254
  end
266
255
  self
267
256
  end
@@ -294,9 +283,9 @@ class Sort
294
283
  self
295
284
  end
296
285
 
297
- def each_entry
286
+ def each_entry(selector)
298
287
  entries = []
299
- @input.each_entry do |path, entry|
288
+ @input.each_entry(selector) do |path, entry|
300
289
  entries << [path, entry]
301
290
  end
302
291
  unless @key_fields.empty?
@@ -542,8 +531,8 @@ class Site
542
531
  self
543
532
  end
544
533
 
545
- def each_entry_path
546
- @combined_output.each_entry_path { |path| yield path }
534
+ def each_entry_path(selector)
535
+ @combined_output.each_entry_path(selector) { |path| yield path }
547
536
  self
548
537
  end
549
538
 
@@ -21,7 +21,6 @@
21
21
  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
22
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
23
 
24
- require 'rubygems'
25
24
  require 'cgi'
26
25
  require 'net/http'
27
26
  require 'uri'
@@ -81,9 +80,12 @@ class CouchDB
81
80
  self
82
81
  end
83
82
 
84
- def each_entry_path
83
+ def each_entry_path(selector)
85
84
  listing = JSON.parse(get_document('_all_docs'))
86
- listing['rows'].each { |row| yield row['id'] }
85
+ listing['rows'].each do |row|
86
+ path = row['id']
87
+ yield path if selector.accept? path
88
+ end
87
89
  self
88
90
  end
89
91
 
@@ -21,7 +21,6 @@
21
21
  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
22
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
23
 
24
- require 'rubygems'
25
24
  require 'yaml'
26
25
  require 'hx'
27
26
  require 'hx/backend/rawfiles'
@@ -73,8 +72,8 @@ class Hobix
73
72
  self
74
73
  end
75
74
 
76
- def each_entry_path
77
- @source.each_entry_path { |path| yield path }
75
+ def each_entry_path(selector)
76
+ @source.each_entry_path(selector) { |path| yield path }
78
77
  self
79
78
  end
80
79
 
@@ -21,7 +21,6 @@
21
21
  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
22
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
23
 
24
- require 'rubygems'
25
24
  require 'pathname'
26
25
  require 'hx'
27
26
 
@@ -56,9 +55,11 @@ class RawFiles
56
55
  self
57
56
  end
58
57
 
59
- def each_entry_path
58
+ def each_entry_path(selector)
60
59
  Pathname.glob(@entry_dir + '**/*') do |pathname|
61
- yield pathname_to_path(pathname) if pathname.file?
60
+ next unless pathname.file?
61
+ path = pathname_to_path(pathname)
62
+ yield path if selector.accept? path
62
63
  end
63
64
  self
64
65
  end
@@ -28,7 +28,7 @@ require 'pathname'
28
28
  require 'tempfile'
29
29
 
30
30
  module Hx
31
- module Commandline
31
+ module CLI
32
32
 
33
33
  DEFAULT_CONFIG_FILENAME = "config.hx"
34
34
 
@@ -105,7 +105,7 @@ end
105
105
 
106
106
  def self.do_gen(site, update_only)
107
107
  output_dir = Hx.get_pathname(site.options, :output_dir)
108
- site.each_entry do |path, entry|
108
+ site.each_entry(Path::ALL) do |path, entry|
109
109
  pathname = output_dir + path
110
110
  content = entry['content']
111
111
  if update_only
@@ -21,7 +21,6 @@
21
21
  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
22
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
23
 
24
- require 'rubygems'
25
24
  require 'hx'
26
25
 
27
26
  module Hx
@@ -35,8 +34,8 @@ class Limit
35
34
  @limit = options[:limit]
36
35
  end
37
36
 
38
- def each_entry
39
- @input.each_entry do |path, entry|
37
+ def each_entry(selector)
38
+ @input.each_entry(selector) do |path, entry|
40
39
  if entry['items']
41
40
  trimmed_entry = entry.dup
42
41
  trimmed_entry['items'] = entry['items'][0...@limit]
@@ -21,7 +21,6 @@
21
21
  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
22
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
23
 
24
- require 'rubygems'
25
24
  require 'hx'
26
25
 
27
26
  module Hx
@@ -35,14 +34,14 @@ class Paginate
35
34
  @page_size = options[:page_size]
36
35
  end
37
36
 
38
- def each_entry
39
- @input.each_entry do |index_path, index_entry|
37
+ def each_entry(selector)
38
+ @input.each_entry(Path::ALL) do |index_path, index_entry|
40
39
  items = index_entry['items'] || []
41
40
  if items.empty?
42
41
  index_entry = index_entry.dup
43
42
  index_entry['pages'] = [index_entry]
44
43
  index_entry['page_index'] = 0
45
- yield index_path, index_entry
44
+ yield index_path, index_entry if selector.accept? index_path
46
45
  else
47
46
  pages = []
48
47
  n_pages = (items.size + @page_size - 1) / @page_size
@@ -65,7 +64,8 @@ class Paginate
65
64
  end
66
65
  pages[-1]['entry'].delete('next_page')
67
66
  pages.each do |page|
68
- yield page['path'], page['entry']
67
+ path = page['path']
68
+ yield path, page['entry'] if selector.accept? path
69
69
  end
70
70
  end
71
71
  end
@@ -21,7 +21,6 @@
21
21
  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
22
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
23
 
24
- require 'rubygems'
25
24
  require 'hx'
26
25
  require 'hx/listing/limit'
27
26
  require 'hx/listing/paginate'
@@ -47,23 +46,31 @@ class RecursiveIndex
47
46
  @input = input
48
47
  end
49
48
 
50
- def each_entry
49
+ def merge_updated(index, entry)
50
+ updated = [entry, index].map { |e| e['updated'] }.compact.max
51
+ index['updated'] = updated if updated
52
+ end
53
+ private :merge_updated
54
+
55
+ def each_entry(selector)
51
56
  indexes = Hash.new { |h,k| h[k] = {'items' => []} }
52
- @input.each_entry do |path, entry|
57
+ @input.each_entry(Path::ALL) do |path, entry|
53
58
  components = path.split("/")
54
- until components.empty?
55
- components.pop
56
- index_path = (components + ["index"]).join("/")
57
- index = indexes[index_path]
58
- index['items'] << {'path' => path, 'entry' => entry}
59
- if entry['updated'] and
60
- (not index['updated'] or entry['updated'] > index['updated'])
61
- index['updated'] = entry['updated']
59
+ if components.last == "index"
60
+ index = indexes[path] = entry.merge(indexes[path])
61
+ merge_updated(index, entry)
62
+ else
63
+ until components.empty?
64
+ components.pop
65
+ index_path = (components + ["index"]).join("/")
66
+ index = indexes[index_path]
67
+ index['items'] << {'path' => path, 'entry' => entry}
68
+ merge_updated(index, entry)
62
69
  end
63
70
  end
64
71
  end
65
72
  indexes.each do |path, entry|
66
- yield path, entry
73
+ yield path, entry if selector.accept? path
67
74
  end
68
75
  self
69
76
  end
@@ -21,7 +21,6 @@
21
21
  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
22
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
23
 
24
- require 'rubygems'
25
24
  require 'time'
26
25
  require 'liquid'
27
26
  require 'redcloth'
@@ -72,7 +71,7 @@ class LiquidTemplate
72
71
  # same template directory for things to work right
73
72
  Liquid::Template.file_system = Liquid::LocalFileSystem.new(template_dir)
74
73
  Liquid::Template.register_filter(TextFilters)
75
- template_file = template_dir + options[:template]
74
+ template_file = template_dir + "#{options[:template]}.liquid"
76
75
  @template = Liquid::Template.parse(template_file.read)
77
76
  @extension = options[:extension]
78
77
  @mime_type = options[:mime_type]
@@ -80,13 +79,10 @@ class LiquidTemplate
80
79
  @strip_extension_re = /\.#{Regexp.quote(@extension)}$/ if @extension
81
80
  end
82
81
 
83
- def each_entry_path
84
- @input.each_entry_path do |path|
85
- unless @extension.nil?
86
- yield "#{path}.#{@extension}"
87
- else
88
- yield path
89
- end
82
+ def each_entry_path(selector)
83
+ @input.each_entry_path(Path::ALL) do |path|
84
+ path = "#{path}.#{@extension}" unless @extension.nil?
85
+ yield path if selector.accept? path
90
86
  end
91
87
  self
92
88
  end
data/lib/hx/path.rb ADDED
@@ -0,0 +1,208 @@
1
+ # hx/path - path selectors
2
+ #
3
+ # Copyright (c) 2009-2010 MenTaLguY <mental@rydia.net>
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.
23
+
24
+ module Hx
25
+ module Path
26
+
27
+ module Selector
28
+ def prefix ; "" ; end
29
+ def suffix ; "" ; end
30
+ def regexp ; nil ; end
31
+ def remaining_suffix(prefix_consumed) ; suffix ; end
32
+
33
+ def accept?(path)
34
+ raise NotImplementedError, "#{self.class}#accept? not implemented"
35
+ end
36
+
37
+ def |(other)
38
+ Disjunction.new(self, other)
39
+ end
40
+
41
+ def &(other)
42
+ Conjunction.new(self, other)
43
+ end
44
+
45
+ def ~()
46
+ Negation.new(self)
47
+ end
48
+ end
49
+
50
+ class All
51
+ include Selector
52
+
53
+ REGEXP = Regexp.new("^.*$")
54
+
55
+ def regexp ; REGEXP ; end
56
+ def accept?(path) ; true ; end
57
+ end
58
+
59
+ ALL = All.new
60
+
61
+ class Literal
62
+ include Selector
63
+
64
+ attr_reader :value
65
+ alias prefix value
66
+
67
+ def initialize(value)
68
+ @value = value
69
+ @regexp = nil
70
+ end
71
+
72
+ def remaining_suffix(prefix_consumed)
73
+ @value[prefix_consumed..-1]
74
+ end
75
+
76
+ def regexp
77
+ @regexp ||= Regexp.new("^#{Regexp.quote(@value)}$")
78
+ end
79
+
80
+ def accept?(path)
81
+ @value == path
82
+ end
83
+ end
84
+
85
+ def self.parse_pattern(pattern_string)
86
+ tokens = []
87
+ pattern_string.scan(/(\*\*?|[^*]+)/) do |token,|
88
+ tokens << case token
89
+ when "**"; :doublestar
90
+ when "*"; :star
91
+ else; token
92
+ end
93
+ end
94
+ prefix = tokens.first
95
+ if tokens.size == 1 and String === prefix
96
+ Literal.new(prefix)
97
+ else
98
+ prefix = "" unless String === prefix
99
+ suffix = tokens.last
100
+ suffix = "" unless String === suffix
101
+ regexp = Regexp.new("^#{tokens.map { |token|
102
+ case token
103
+ when :doublestar; '.*'
104
+ when :star; '[^/]*'
105
+ else; Regexp.quote(token)
106
+ end
107
+ }}$")
108
+ Pattern.new(regexp, prefix, suffix)
109
+ end
110
+ end
111
+
112
+ def self.literal(literal_string)
113
+ Literal.new(literal_string)
114
+ end
115
+
116
+ class Pattern
117
+ include Selector
118
+
119
+ attr_reader :prefix
120
+ attr_reader :suffix
121
+ attr_reader :regexp
122
+
123
+ def initialize(regexp, prefix, suffix)
124
+ @regexp = regexp
125
+ @prefix = prefix
126
+ @suffix = suffix
127
+ end
128
+
129
+ def accept?(path)
130
+ !!(@regexp.match(path))
131
+ end
132
+ end
133
+
134
+ module Connective
135
+ include Selector
136
+
137
+ attr_reader :prefix
138
+ attr_reader :suffix
139
+
140
+ def initialize(*selectors)
141
+ @selectors = selectors
142
+ prefixes = selectors.map { |s| s.prefix }
143
+ prefix_length = (prefixes.first || "").length
144
+ prefixes.each_cons(2) do |a, b|
145
+ prefix_length.downto(0) do |length|
146
+ prefix_length = length
147
+ break if a[0...length] == b[0...length]
148
+ end
149
+ end
150
+ @prefix = prefixes.first[0...prefix_length]
151
+ suffixes = selectors.map { |s| s.remaining_suffix(prefix_length) }
152
+ suffix_length = (suffixes.first || "").length
153
+ suffixes.each_cons(2) do |a, b|
154
+ suffix_length.downto(0) do |length|
155
+ suffix_length = length
156
+ break if a[-length..-1] == b[-length..-1]
157
+ end
158
+ end
159
+ @suffix = suffixes.first[-suffix_length..-1]
160
+ end
161
+ end
162
+
163
+ class Conjunction
164
+ include Connective
165
+
166
+ def accept?(path)
167
+ @selectors.all? { |s| s.accept? path }
168
+ end
169
+ end
170
+
171
+ class Disjunction
172
+ include Connective
173
+
174
+ attr_reader :regexp
175
+
176
+ def initialize(*selectors)
177
+ super
178
+ regexps = @selectors.map { |s| s.regexp }
179
+ if regexps.all?
180
+ @regexp = Regexp.union(*regexps)
181
+ else
182
+ @regexp = nil
183
+ end
184
+ end
185
+
186
+ def accept?(path)
187
+ if @regexp
188
+ !!@regexp.match(path)
189
+ else
190
+ @selectors.any? { |s| s.accept? path }
191
+ end
192
+ end
193
+ end
194
+
195
+ class Negation
196
+ include Selector
197
+
198
+ def initialize(selector)
199
+ @selector = selector
200
+ end
201
+
202
+ def accept?(path)
203
+ not @selector.accept?(path)
204
+ end
205
+ end
206
+
207
+ end
208
+ end
@@ -21,7 +21,6 @@
21
21
  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
22
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
23
 
24
- require 'rubygems'
25
24
  require 'cgi'
26
25
  require 'hx'
27
26
  require 'rack/mime'
data/loc.sh ADDED
@@ -0,0 +1,3 @@
1
+ #!/bin/sh
2
+ # swunked from wmorgan
3
+ (echo bin/hx ; find lib -name '*.rb' ) | xargs cat | grep -v "^ *$" | grep -v "^ *#"| grep -v "^ *end *$"| wc -l
data/spec/cache_spec.rb CHANGED
@@ -12,20 +12,20 @@ describe Hx::Cache do
12
12
  end
13
13
 
14
14
  it "should return itself from each_entry" do
15
- @cache.each_entry {}.should == @cache
15
+ @cache.each_entry(Hx::Path::ALL) {}.should == @cache
16
16
  end
17
17
 
18
18
  it "enumerates the same entries from the source" do
19
- @cache.each_entry do |path, entry|
19
+ @cache.each_entry(Hx::Path::ALL) do |path, entry|
20
20
  entry.should == @source.get_entry(path)
21
21
  end
22
22
  end
23
23
 
24
24
  it "only reads the source once" do
25
- @cache.each_entry {}
25
+ @cache.each_entry(Hx::Path::ALL) {}
26
26
  def @source.each_entry
27
27
  raise RuntimeError, "should not be called"
28
28
  end
29
- @cache.each_entry {}
29
+ @cache.each_entry(Hx::Path::ALL) {}
30
30
  end
31
31
  end
@@ -8,11 +8,11 @@ describe Hx::NullInput do
8
8
  end
9
9
 
10
10
  it "should return itself from each_entry" do
11
- @null_source.each_entry {}.should == @null_source
11
+ @null_source.each_entry(Hx::Path::ALL) {}.should == @null_source
12
12
  end
13
13
 
14
14
  it "enumerates no entry paths" do
15
- @null_source.each_entry do |path, entry|
15
+ @null_source.each_entry(Hx::Path::ALL) do |path, entry|
16
16
  raise RuntimeError("No entries")
17
17
  end
18
18
  end
data/spec/overlay_spec.rb CHANGED
@@ -14,25 +14,25 @@ describe Hx::Overlay do
14
14
  end
15
15
 
16
16
  it "should return itself from each_entry" do
17
- @overlay.each_entry {}.should == @overlay
17
+ @overlay.each_entry(Hx::Path::ALL) {}.should == @overlay
18
18
  end
19
19
 
20
20
  it "should expose the union of paths" do
21
21
  actual_paths = []
22
- @overlay.each_entry do |path, entry|
22
+ @overlay.each_entry(Hx::Path::ALL) do |path, entry|
23
23
  actual_paths << path
24
24
  end
25
25
  actual_paths.sort.should == %w(foo bar baz).sort
26
26
  end
27
27
 
28
28
  it "should give earlier sources precedence" do
29
- @overlay.each_entry do |path, entry|
29
+ @overlay.each_entry(Hx::Path::ALL) do |path, entry|
30
30
  entry.should == @a.get_entry('bar') if path == 'bar'
31
31
  end
32
32
  end
33
33
 
34
34
  it "should expose entries from all sources" do
35
- @overlay.each_entry do |path, entry|
35
+ @overlay.each_entry(Hx::Path::ALL) do |path, entry|
36
36
  entry.should == @a.get_entry('foo') if path == 'foo'
37
37
  entry.should == @b.get_entry('baz') if path == 'baz'
38
38
  end
data/spec/pathops_spec.rb CHANGED
@@ -14,12 +14,12 @@ describe Hx::AddPath do
14
14
  end
15
15
 
16
16
  it "should return itself from each_entry" do
17
- @add.each_entry {}.should == @add
17
+ @add.each_entry(Hx::Path::ALL) {}.should == @add
18
18
  end
19
19
 
20
20
  it "yields augmented paths from each_entry" do
21
21
  paths = Set[]
22
- @add.each_entry do |path, entry|
22
+ @add.each_entry(Hx::Path::ALL) do |path, entry|
23
23
  paths.add path
24
24
  end
25
25
  paths.should == @after_paths
@@ -36,12 +36,12 @@ describe Hx::StripPath do
36
36
  end
37
37
 
38
38
  it "should return itself from each_entry" do
39
- @strip.each_entry {}.should == @strip
39
+ @strip.each_entry(Hx::Path::ALL) {}.should == @strip
40
40
  end
41
41
 
42
42
  it "yields stripped paths from each_entry" do
43
43
  paths = Set[]
44
- @strip.each_entry do |path, entry|
44
+ @strip.each_entry(Hx::Path::ALL) do |path, entry|
45
45
  paths.add path
46
46
  end
47
47
  paths.should == @after_paths
@@ -59,12 +59,12 @@ describe Hx::PathSubset do
59
59
  end
60
60
 
61
61
  it "returns itself from each_entry" do
62
- @subset.each_entry {}.should == @subset
62
+ @subset.each_entry(Hx::Path::ALL) {}.should == @subset
63
63
  end
64
64
 
65
65
  it "enumerates paths according to the subset filter" do
66
66
  actual_paths = Set[]
67
- @subset.each_entry do |path, entry|
67
+ @subset.each_entry(Hx::Path::ALL) do |path, entry|
68
68
  actual_paths.add path
69
69
  end
70
70
  actual_paths.should == Set['foo/bar', 'foo/baz']
data/spec/rack_spec.rb CHANGED
@@ -3,11 +3,13 @@ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
3
3
  require 'hx'
4
4
  require 'hx/rack'
5
5
  require 'rack/mock'
6
+ require 'rack/lint'
6
7
 
7
8
  describe Hx::Rack::Application do
8
9
  before(:each) do
9
10
  @input = FakeInput.new
10
11
  @app = Hx::Rack::Application.new(@input, {})
12
+ @app = Rack::Lint.new(@app)
11
13
  @service = Rack::MockRequest.new(@app)
12
14
  end
13
15
 
@@ -0,0 +1,150 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ require 'hx/path'
4
+
5
+ describe Hx::Path::Pattern do
6
+ it "should include Hx::Path::Selector" do
7
+ Hx::Path::Pattern.should < Hx::Path::Selector
8
+ end
9
+
10
+ it "should parse pattern strings" do
11
+ cases = [ { :pattern => "foo/**/bar",
12
+ :prefix => "foo/",
13
+ :suffix => "/bar" },
14
+ { :pattern => "foo/*/bar",
15
+ :prefix => "foo/",
16
+ :suffix => "/bar" },
17
+ { :pattern => "[blah",
18
+ :prefix => "[blah",
19
+ :suffix => "" },
20
+ { :pattern => "hoge*",
21
+ :prefix => "hoge",
22
+ :suffix => "" },
23
+ { :pattern => "*hoge",
24
+ :prefix => "",
25
+ :suffix => "hoge" } ]
26
+ for test_case in cases
27
+ pattern = Hx::Path.parse_pattern(test_case[:pattern])
28
+ pattern.prefix.should == test_case[:prefix]
29
+ pattern.suffix.should == test_case[:suffix]
30
+ end
31
+ end
32
+
33
+ it "should accept or reject paths" do
34
+ pattern = Hx::Path.parse_pattern("foo/bar")
35
+ pattern.should accept("foo/bar")
36
+ pattern.should_not accept("foo/baz")
37
+ end
38
+
39
+ it "should provide an equivalent regexp" do
40
+ pattern = Hx::Path.parse_pattern("foo/bar")
41
+ pattern.regexp.should_not be_nil
42
+ pattern.regexp.match("foo/bar").should_not be_nil
43
+ pattern.regexp.match("foo/bar/baz").should be_nil
44
+
45
+ pattern = Hx::Path.parse_pattern("foo/*/baz")
46
+ pattern.regexp.should_not be_nil
47
+ pattern.regexp.match("foo/baz").should be_nil
48
+ pattern.regexp.match("foo/bar/baz").should_not be_nil
49
+ end
50
+ end
51
+
52
+ PATHFILTER_PREFIX_CASES = [
53
+ { :patterns => ["foo/bar", "fudge/bear"],
54
+ :prefix => "f",
55
+ :suffix => "ar" },
56
+ { :patterns => ["foo/*/bar", "fudge/*/bear"],
57
+ :prefix => "f",
58
+ :suffix => "ar" },
59
+ { :patterns => ["foo/bar", "fudge/*/bear"],
60
+ :prefix => "f",
61
+ :suffix => "ar" },
62
+ { :patterns => ["bar*", "*bear"],
63
+ :prefix => "",
64
+ :suffix => "" },
65
+ { :patterns => ["foobarbaz", "foobar*barbaz"],
66
+ :prefix => "foobar",
67
+ :suffix => "baz" },
68
+ { :patterns => ["foobarbaz*", "foobar*barbaz"],
69
+ :prefix => "foobar",
70
+ :suffix => "" },
71
+ { :patterns => ["*foobarbaz", "foobar*barbaz"],
72
+ :prefix => "",
73
+ :suffix => "barbaz" }
74
+ ]
75
+
76
+ describe "Hx::Path::Selector disjunctions" do
77
+ it "should be possible" do
78
+ filters = ["foo/bar", "abcdefg"].map { |p| Hx::Path.parse_pattern(p) }
79
+ filter = filters.inject { |a, b| a | b }
80
+ filter.should be_a_kind_of(Hx::Path::Selector)
81
+ end
82
+
83
+ it "should accept and reject the right paths" do
84
+ filters = ["foo/bar", "abcdefg"].map { |p| Hx::Path.parse_pattern(p) }
85
+ filter = filters.inject { |a, b| a | b }
86
+ filter.should accept("foo/bar")
87
+ filter.should accept("abcdefg")
88
+ filter.should_not accept("hoge")
89
+ end
90
+
91
+ for test_case_outer in PATHFILTER_PREFIX_CASES
92
+ Proc.new do |test_case|
93
+ it "should produce optimal prefixes and suffixes" do
94
+ filters = test_case[:patterns].map { |p| Hx::Path.parse_pattern(p) }
95
+ filter = filters.inject { |a, b| a | b }
96
+ filter.prefix.should == test_case[:prefix]
97
+ filter.suffix.should == test_case[:suffix]
98
+ end
99
+ end.call(test_case_outer)
100
+ end
101
+ end
102
+
103
+ describe "negated Hx::Path::Selectors" do
104
+ before :each do
105
+ @pattern = ~Hx::Path.parse_pattern("foo*bar")
106
+ end
107
+
108
+ it "should be possible" do
109
+ @pattern.should be_a_kind_of(Hx::Path::Selector)
110
+ end
111
+
112
+ it "should reject what they match" do
113
+ @pattern.should_not accept("foobar")
114
+ @pattern.should accept("hoge")
115
+ end
116
+
117
+ it "should have empty prefix and suffix" do
118
+ @pattern.prefix.should == ""
119
+ @pattern.suffix.should == ""
120
+ end
121
+ end
122
+
123
+ describe "Hx::Path::Selector conjunctions" do
124
+ it "should be possible" do
125
+ filters = ["foo*", "*bar"].map { |p| Hx::Path.parse_pattern(p) }
126
+ filter = filters.inject { |a, b| a & b }
127
+ filter.should be_a_kind_of(Hx::Path::Selector)
128
+ end
129
+
130
+ it "should accept only paths matching both filters" do
131
+ filters = ["foo*", "*bar"].map { |p| Hx::Path.parse_pattern(p) }
132
+ filter = filters.inject { |a, b| a & b }
133
+ filter.should accept("foobar")
134
+ filter.should accept("fooxbar")
135
+ filter.should_not accept("lemur")
136
+ filter.should_not accept("foobear")
137
+ filter.should_not accept("rebar")
138
+ end
139
+
140
+ for test_case_outer in PATHFILTER_PREFIX_CASES
141
+ Proc.new do |test_case|
142
+ it "should produce optimal prefixes and suffixes" do
143
+ filters = test_case[:patterns].map { |p| Hx::Path.parse_pattern(p) }
144
+ filter = filters.inject { |a, b| a & b }
145
+ filter.prefix.should == test_case[:prefix]
146
+ filter.suffix.should == test_case[:suffix]
147
+ end
148
+ end.call(test_case_outer)
149
+ end
150
+ end
data/spec/site_spec.rb CHANGED
@@ -60,6 +60,6 @@ describe Hx::Site do
60
60
  end
61
61
 
62
62
  it "returns itself from each_entry" do
63
- @site.each_entry {}.should == @site
63
+ @site.each_entry(Hx::Path::ALL) {}.should == @site
64
64
  end
65
65
  end
data/spec/spec_helper.rb CHANGED
@@ -4,6 +4,10 @@ require 'hx'
4
4
  require 'ostruct'
5
5
  require 'spec'
6
6
  require 'spec/autorun'
7
+ begin
8
+ require 'rubygems'
9
+ rescue LoadError
10
+ end
7
11
 
8
12
  class FakeInput
9
13
  include Hx::Filter
@@ -18,8 +22,8 @@ class FakeInput
18
22
  @entries[path] = entry
19
23
  end
20
24
 
21
- def each_entry_path
22
- @entries.each_key { |path| yield path }
25
+ def each_entry_path(selector)
26
+ @entries.each_key { |path| yield path if selector.accept? path }
23
27
  self
24
28
  end
25
29
 
@@ -32,6 +36,12 @@ class FakeInput
32
36
  end
33
37
  end
34
38
 
39
+ Spec::Matchers.define :accept do |value|
40
+ match do |acceptor|
41
+ acceptor.accept?(value)
42
+ end
43
+ end
44
+
35
45
  Spec::Runner.configure do |config|
36
46
 
37
47
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hx
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.4
4
+ version: 0.8.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - MenTaLguY
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-02-16 00:00:00 -05:00
12
+ date: 2010-04-25 00:00:00 -04:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -48,21 +48,23 @@ files:
48
48
  - lib/hx/backend/couchdb.rb
49
49
  - lib/hx/backend/hobix.rb
50
50
  - lib/hx/backend/rawfiles.rb
51
- - lib/hx/commandline.rb
51
+ - lib/hx/cli.rb
52
52
  - lib/hx/listing/limit.rb
53
53
  - lib/hx/listing/paginate.rb
54
54
  - lib/hx/listing/recursiveindex.rb
55
55
  - lib/hx/output/liquidtemplate.rb
56
+ - lib/hx/path.rb
56
57
  - lib/hx/rack.rb
57
58
  - lib/hx/rack/application.rb
59
+ - loc.sh
58
60
  - spec/cache_spec.rb
59
61
  - spec/hx_dummy.rb
60
62
  - spec/hx_dummy2.rb
61
63
  - spec/nullinput_spec.rb
62
64
  - spec/overlay_spec.rb
63
- - spec/pathfilter_spec.rb
64
65
  - spec/pathops_spec.rb
65
66
  - spec/rack_spec.rb
67
+ - spec/selector_spec.rb
66
68
  - spec/site_spec.rb
67
69
  - spec/spec.opts
68
70
  - spec/spec_helper.rb
@@ -98,10 +100,10 @@ test_files:
98
100
  - spec/spec_helper.rb
99
101
  - spec/cache_spec.rb
100
102
  - spec/hx_dummy.rb
101
- - spec/pathfilter_spec.rb
102
103
  - spec/nullinput_spec.rb
103
104
  - spec/site_spec.rb
104
105
  - spec/hx_dummy2.rb
105
106
  - spec/rack_spec.rb
106
107
  - spec/overlay_spec.rb
107
108
  - spec/pathops_spec.rb
109
+ - spec/selector_spec.rb
@@ -1,45 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
-
3
- require 'hx'
4
-
5
- describe Hx::PathSubset::Predicate do
6
- it "accepts anything when the accept and reject patterns are nil" do
7
- filter = Hx::PathSubset::Predicate.new(nil, nil)
8
- filter.accept?("blah").should be_true
9
- end
10
-
11
- it "accepts only paths matching the accept pattern when it is specified" do
12
- filter = Hx::PathSubset::Predicate.new("foo", nil)
13
- filter.accept?("foo").should be_true
14
- filter.accept?("bar").should be_false
15
- end
16
-
17
- it "accepts only paths not matching reject, when only that is specified" do
18
- filter = Hx::PathSubset::Predicate.new(nil, "foo")
19
- filter.accept?("foo").should be_false
20
- filter.accept?("bar").should be_true
21
- end
22
-
23
- it "matches the difference of accept and reject when both are specified" do
24
- filter = Hx::PathSubset::Predicate.new("foo/*", "foo/bar")
25
- filter.accept?("foo/bar").should be_false
26
- filter.accept?("foo/baz").should be_true
27
- filter.accept?("bar").should be_false
28
- end
29
-
30
- it "shouldn't match across slashes with single star" do
31
- filter = Hx::PathSubset::Predicate.new("foo/*", nil)
32
- filter.accept?("bar").should be_false
33
- filter.accept?("foo").should be_false
34
- filter.accept?("foo/bar").should be_true
35
- filter.accept?("foo/bar/baz").should be_false
36
- end
37
-
38
- it "should match across slashes with double star" do
39
- filter = Hx::PathSubset::Predicate.new("foo/**", nil)
40
- filter.accept?("bar").should be_false
41
- filter.accept?("foo").should be_false
42
- filter.accept?("foo/bar").should be_true
43
- filter.accept?("foo/bar/baz").should be_true
44
- end
45
- end