hx 0.7.4 → 0.8.2

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