valise 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/doc/README +69 -0
- data/doc/Specification +72 -0
- data/doc/Specifications +81 -0
- data/doc/coverage/assets/0.5.3/app.js +88 -0
- data/doc/coverage/assets/0.5.3/fancybox/blank.gif +0 -0
- data/doc/coverage/assets/0.5.3/fancybox/fancy_close.png +0 -0
- data/doc/coverage/assets/0.5.3/fancybox/fancy_loading.png +0 -0
- data/doc/coverage/assets/0.5.3/fancybox/fancy_nav_left.png +0 -0
- data/doc/coverage/assets/0.5.3/fancybox/fancy_nav_right.png +0 -0
- data/doc/coverage/assets/0.5.3/fancybox/fancy_shadow_e.png +0 -0
- data/doc/coverage/assets/0.5.3/fancybox/fancy_shadow_n.png +0 -0
- data/doc/coverage/assets/0.5.3/fancybox/fancy_shadow_ne.png +0 -0
- data/doc/coverage/assets/0.5.3/fancybox/fancy_shadow_nw.png +0 -0
- data/doc/coverage/assets/0.5.3/fancybox/fancy_shadow_s.png +0 -0
- data/doc/coverage/assets/0.5.3/fancybox/fancy_shadow_se.png +0 -0
- data/doc/coverage/assets/0.5.3/fancybox/fancy_shadow_sw.png +0 -0
- data/doc/coverage/assets/0.5.3/fancybox/fancy_shadow_w.png +0 -0
- data/doc/coverage/assets/0.5.3/fancybox/fancy_title_left.png +0 -0
- data/doc/coverage/assets/0.5.3/fancybox/fancy_title_main.png +0 -0
- data/doc/coverage/assets/0.5.3/fancybox/fancy_title_over.png +0 -0
- data/doc/coverage/assets/0.5.3/fancybox/fancy_title_right.png +0 -0
- data/doc/coverage/assets/0.5.3/fancybox/fancybox-x.png +0 -0
- data/doc/coverage/assets/0.5.3/fancybox/fancybox-y.png +0 -0
- data/doc/coverage/assets/0.5.3/fancybox/fancybox.png +0 -0
- data/doc/coverage/assets/0.5.3/fancybox/jquery.fancybox-1.3.1.css +363 -0
- data/doc/coverage/assets/0.5.3/fancybox/jquery.fancybox-1.3.1.pack.js +44 -0
- data/doc/coverage/assets/0.5.3/favicon_green.png +0 -0
- data/doc/coverage/assets/0.5.3/favicon_red.png +0 -0
- data/doc/coverage/assets/0.5.3/favicon_yellow.png +0 -0
- data/doc/coverage/assets/0.5.3/highlight.css +129 -0
- data/doc/coverage/assets/0.5.3/highlight.pack.js +1 -0
- data/doc/coverage/assets/0.5.3/jquery-1.6.2.min.js +18 -0
- data/doc/coverage/assets/0.5.3/jquery.dataTables.min.js +152 -0
- data/doc/coverage/assets/0.5.3/jquery.timeago.js +141 -0
- data/doc/coverage/assets/0.5.3/jquery.url.js +174 -0
- data/doc/coverage/assets/0.5.3/loading.gif +0 -0
- data/doc/coverage/assets/0.5.3/magnify.png +0 -0
- data/doc/coverage/assets/0.5.3/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- data/doc/coverage/assets/0.5.3/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
- data/doc/coverage/assets/0.5.3/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
- data/doc/coverage/assets/0.5.3/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/doc/coverage/assets/0.5.3/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
- data/doc/coverage/assets/0.5.3/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- data/doc/coverage/assets/0.5.3/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
- data/doc/coverage/assets/0.5.3/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- data/doc/coverage/assets/0.5.3/smoothness/images/ui-icons_222222_256x240.png +0 -0
- data/doc/coverage/assets/0.5.3/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
- data/doc/coverage/assets/0.5.3/smoothness/images/ui-icons_454545_256x240.png +0 -0
- data/doc/coverage/assets/0.5.3/smoothness/images/ui-icons_888888_256x240.png +0 -0
- data/doc/coverage/assets/0.5.3/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
- data/doc/coverage/assets/0.5.3/smoothness/jquery-ui-1.8.4.custom.css +295 -0
- data/doc/coverage/assets/0.5.3/stylesheet.css +383 -0
- data/doc/coverage/index.html +5843 -0
- data/doc/coverage/jquery-1.3.2.min.js +19 -0
- data/doc/coverage/jquery.tablesorter.min.js +15 -0
- data/doc/coverage/lib-fileset-errors_rb.html +231 -0
- data/doc/coverage/lib-fileset-item_rb.html +303 -0
- data/doc/coverage/lib-fileset-populator_rb.html +249 -0
- data/doc/coverage/lib-fileset-search-root_rb.html +1053 -0
- data/doc/coverage/lib-fileset-utils_rb.html +453 -0
- data/doc/coverage/lib-fileset_rb.html +789 -0
- data/doc/coverage/print.css +12 -0
- data/doc/coverage/rcov.js +42 -0
- data/doc/coverage/screen.css +270 -0
- data/lib/valise/errors.rb +30 -0
- data/lib/valise/item.rb +105 -0
- data/lib/valise/path-matcher.rb +113 -0
- data/lib/valise/search-root.rb +166 -0
- data/lib/valise/stack.rb +169 -0
- data/lib/valise/stem-decorator.rb +56 -0
- data/lib/valise/utils.rb +65 -0
- data/lib/valise.rb +219 -0
- data/spec/addable.rb +41 -0
- data/spec/dump_load.rb +32 -0
- data/spec/error_handling.rb +9 -0
- data/spec/fileset.rb +170 -0
- data/spec/glob_handling.rb +28 -0
- data/spec/item.rb +35 -0
- data/spec/merge_diff.rb +93 -0
- data/spec/population.rb +51 -0
- data/spec/search_root.rb +24 -0
- data/spec/stemming.rb +33 -0
- data/spec_help/file-sandbox.rb +164 -0
- data/spec_help/gem_test_suite.rb +17 -0
- data/spec_help/spec_helper.rb +3 -0
- data/spec_help/ungemmer.rb +37 -0
- metadata +220 -0
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
require 'valise/errors'
|
|
2
|
+
require 'valise/utils'
|
|
3
|
+
require 'valise/item'
|
|
4
|
+
require 'fileutils'
|
|
5
|
+
#require 'enum'
|
|
6
|
+
|
|
7
|
+
module Valise
|
|
8
|
+
class SearchRoot
|
|
9
|
+
include Unpath
|
|
10
|
+
include Enumerable
|
|
11
|
+
|
|
12
|
+
def initialize(path)
|
|
13
|
+
@segments = unpath(path)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
attr_reader :segments
|
|
17
|
+
|
|
18
|
+
def each
|
|
19
|
+
paths = [[]]
|
|
20
|
+
until paths.empty?
|
|
21
|
+
rel = paths.shift
|
|
22
|
+
path = repath(@segments + rel)
|
|
23
|
+
#symlinks?
|
|
24
|
+
if(File::directory?(path))
|
|
25
|
+
Dir.entries(path).each do |entry|
|
|
26
|
+
next if entry == "."
|
|
27
|
+
next if entry == ".."
|
|
28
|
+
paths.push(rel + [entry])
|
|
29
|
+
end
|
|
30
|
+
elsif(File::file?(path))
|
|
31
|
+
yield(unpath(rel))
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def inspect
|
|
37
|
+
"#{self.class.name.split(":").last}:#{[*@segments].join("/")}"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def full_path(segments)
|
|
41
|
+
repath(@segments + segments)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def write(item)
|
|
45
|
+
return if item.contents.nil?
|
|
46
|
+
FileUtils::mkdir_p(::File::dirname(item.full_path))
|
|
47
|
+
File::open(item.full_path, "w") do |file|
|
|
48
|
+
file.write(item.dump_contents)
|
|
49
|
+
end
|
|
50
|
+
item
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def writable?(segments)
|
|
54
|
+
path = full_path(segments)
|
|
55
|
+
return (!File::exists?(path) or File::writable?(path))
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def present?(segments)
|
|
59
|
+
return File::exists?(full_path(segments))
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def insert(item)
|
|
63
|
+
if(File::exists?(item.full_path))
|
|
64
|
+
raise WouldClobber.new(Item.new(item.stack.segments, self, nil))
|
|
65
|
+
else
|
|
66
|
+
write(item)
|
|
67
|
+
return item
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def get_from(item)
|
|
72
|
+
File::read(item.full_path)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
class ReadOnlySearchRoot < SearchRoot
|
|
77
|
+
def writable?(segments)
|
|
78
|
+
false
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def write(item)
|
|
82
|
+
raise ReadOnly
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def insert(item)
|
|
86
|
+
raise ReadOnly
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
class DefinedDefaults < ReadOnlySearchRoot
|
|
91
|
+
def initialize
|
|
92
|
+
@files = {}
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def segments
|
|
96
|
+
raise VirtualSearchPath, "does not have a real path"
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def present?(segments)
|
|
100
|
+
@files.has_key?(segments)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def each
|
|
104
|
+
@files.each_key do |path|
|
|
105
|
+
yield(unpath(path))
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def get_from(item)
|
|
110
|
+
return @files[item.segments]
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def define(&block)
|
|
114
|
+
unless block.nil?
|
|
115
|
+
definer = DefinitionHelper.new(self)
|
|
116
|
+
definer.instance_eval &block
|
|
117
|
+
end
|
|
118
|
+
return self
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def self.define(&block)
|
|
122
|
+
return self.new.define(&block)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def add_item(path, contents)
|
|
126
|
+
check_path = path[0..-2]
|
|
127
|
+
until check_path.empty?
|
|
128
|
+
if @files.has_key?(check_path)
|
|
129
|
+
raise MalformedTree, "Tried to add items below #{path[0..-2]} which is not a directory"
|
|
130
|
+
end
|
|
131
|
+
check_path.pop
|
|
132
|
+
end
|
|
133
|
+
@files[path] = contents
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def add_dir(path)
|
|
137
|
+
#add_item(path, ValiseWorks::Directory.new(path))
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def add_file(path, data = nil)
|
|
141
|
+
add_item(path, data)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
class DefinitionHelper
|
|
145
|
+
def initialize(target)
|
|
146
|
+
@target = target
|
|
147
|
+
@prefix = []
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def dir(name)
|
|
151
|
+
path = @prefix + [name]
|
|
152
|
+
@target.add_dir(path)
|
|
153
|
+
@prefix.push(name)
|
|
154
|
+
yield if block_given?
|
|
155
|
+
@prefix.pop
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def file(name, data=nil)
|
|
159
|
+
path = @prefix + [name.to_s]
|
|
160
|
+
@target.add_file(path, data)
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
include StringTools
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
data/lib/valise/stack.rb
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# New plan here: Stack is returned by "find" and responds to many of the
|
|
2
|
+
# methods that Item current responds to. It caches results, essentially, of
|
|
3
|
+
# the find search - maybe at creation, with possiblity for reload/update.
|
|
4
|
+
# Also, makes possible use cases like file promotion, merging, etc.
|
|
5
|
+
#
|
|
6
|
+
# Still need to resolve the interface for parsing and merging files.
|
|
7
|
+
#
|
|
8
|
+
# * something from the Valise definition - would have to be part of defaults,
|
|
9
|
+
# which further is a problem if a file doesn't exist in the defaults
|
|
10
|
+
#
|
|
11
|
+
# * a filetype mapping
|
|
12
|
+
#
|
|
13
|
+
# * Is it a find-time user thing
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
module Valise
|
|
17
|
+
module ItemEnum
|
|
18
|
+
include Enumerable
|
|
19
|
+
|
|
20
|
+
class Enumerator
|
|
21
|
+
include ItemEnum
|
|
22
|
+
|
|
23
|
+
def initialize(list, &filter)
|
|
24
|
+
@list = list
|
|
25
|
+
@filter = proc(&filter)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def each
|
|
29
|
+
@list.each do |item|
|
|
30
|
+
next unless @filter[item]
|
|
31
|
+
yield(item)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def writable
|
|
37
|
+
Enumerator.new(self) do |item|
|
|
38
|
+
item.writable?
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def absent
|
|
43
|
+
Enumerator.new(self) do |item|
|
|
44
|
+
not item.present?
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def present
|
|
49
|
+
Enumerator.new(self) do |item|
|
|
50
|
+
item.present?
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
class MergeDiff
|
|
56
|
+
@@classes = {}
|
|
57
|
+
def self.[](index)
|
|
58
|
+
@@classes[index]
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def self.register(index)
|
|
62
|
+
@@classes[index] = self
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def initialize(stack)
|
|
66
|
+
@stack = stack
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
class TopMost < MergeDiff
|
|
70
|
+
register :topmost
|
|
71
|
+
|
|
72
|
+
def merge(item)
|
|
73
|
+
item.load_contents
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def diff(item, value)
|
|
77
|
+
value
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
class HashMerge < MergeDiff
|
|
82
|
+
register :hash_merge
|
|
83
|
+
|
|
84
|
+
def merge(item)
|
|
85
|
+
merge_stack(@stack.not_above(item).reverse)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def merge_stack(stack)
|
|
89
|
+
stack.inject({}) do |hash, item|
|
|
90
|
+
deep_merge(hash, item.load_contents)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def deep_merge(collect, item)
|
|
95
|
+
item.each_pair do |key, value|
|
|
96
|
+
case value
|
|
97
|
+
when Hash
|
|
98
|
+
collect[key] ||= {}
|
|
99
|
+
deep_merge(collect[key], value)
|
|
100
|
+
else
|
|
101
|
+
collect[key] = value
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
collect
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def diff(item, new_contents)
|
|
108
|
+
diff_with = merge_stack(@stack.below(item).reverse)
|
|
109
|
+
result = new_contents.dup
|
|
110
|
+
|
|
111
|
+
diff_with.each_pair do |key, value|
|
|
112
|
+
if result.has_key?(key)
|
|
113
|
+
if result[key] == value
|
|
114
|
+
result.delete(key)
|
|
115
|
+
end
|
|
116
|
+
else
|
|
117
|
+
result[key] = nil
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
result
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
class Stack
|
|
127
|
+
include Unpath
|
|
128
|
+
include ItemEnum
|
|
129
|
+
|
|
130
|
+
def inspect
|
|
131
|
+
"<default>:#{@segments.join "/"} #{@valise.inspect}"
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def initialize(path, set, merge_class, dump_load)
|
|
135
|
+
@segments = unpath(path)
|
|
136
|
+
@valise = set
|
|
137
|
+
@merge_diff = (merge_class || MergeDiff::TopMost).new(self)
|
|
138
|
+
@dump_load = dump_load
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
attr_reader :segments
|
|
142
|
+
|
|
143
|
+
def merged(item)
|
|
144
|
+
@merge_diff.merge(item)
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def diffed(item, value)
|
|
148
|
+
@merge_diff.diff(item, value)
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def not_above(item)
|
|
152
|
+
Stack.new(@segments, @valise.not_above(item.root), @merge_diff.class, @dump_load)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def below(item)
|
|
156
|
+
Stack.new(@segments, @valise.below(item.root), @merge_diff.class, @dump_load)
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def each
|
|
160
|
+
@valise.each do |root|
|
|
161
|
+
yield(Item.new(self, root, @dump_load))
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def reverse
|
|
166
|
+
Stack.new(@segments, @valise.reverse, @merge_diff.class, @dump_load)
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
module Valise
|
|
2
|
+
class StemDecorator < SearchRoot
|
|
3
|
+
def initialize(stem, search_root)
|
|
4
|
+
@stem, @search_root = stem, search_root
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def under_stem(path)
|
|
8
|
+
segments = unpath(path)
|
|
9
|
+
top_part = segments[0...@stem.length]
|
|
10
|
+
if top_part == @stem
|
|
11
|
+
return segments[@stem.length..-1]
|
|
12
|
+
else
|
|
13
|
+
raise PathOutsideOfRoot
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def inspect
|
|
18
|
+
"#{self.class.name.split(":").last}:[#{@stem.join("/")}]#{@search_root.inspect}"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def each
|
|
22
|
+
@search_root.each do |path|
|
|
23
|
+
yield(@stem + path)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def full_path(segments)
|
|
28
|
+
segments = under_stem(segments)
|
|
29
|
+
@search_root.full_path(segments)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def write(item)
|
|
33
|
+
@search_root.write(item)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def writable?(segments)
|
|
37
|
+
@search_root.writable?(under_stem(segments))
|
|
38
|
+
rescue PathOutsideOfRoot
|
|
39
|
+
return false
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def present?(segments)
|
|
43
|
+
@search_root.present?(under_stem(segments))
|
|
44
|
+
rescue PathOutsideOfRoot
|
|
45
|
+
return false
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def insert(item)
|
|
49
|
+
@search_root.insert(item)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def get_from(item)
|
|
53
|
+
@search_root.get_from(item)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
data/lib/valise/utils.rb
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
module Valise
|
|
2
|
+
module StringTools
|
|
3
|
+
def align(string)
|
|
4
|
+
lines = string.split(/\n/)
|
|
5
|
+
first = lines.shift
|
|
6
|
+
match = /^(\s*)<<</.match(first)
|
|
7
|
+
unless(match.nil?)
|
|
8
|
+
catch(:haircut) do
|
|
9
|
+
return lines.map do |line|
|
|
10
|
+
raise line if /^#{match[1]}|^\s*$/ !~ line
|
|
11
|
+
throw :haircut if /^#{match[1]}|^\s*$/ !~ line
|
|
12
|
+
line.sub(/^#{match[1]}/, "")
|
|
13
|
+
end.join("\n")
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
return string
|
|
17
|
+
end
|
|
18
|
+
module_function :align
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
module Unpath
|
|
22
|
+
def unpath(parts)
|
|
23
|
+
if Array === parts and parts.length == 1
|
|
24
|
+
parts = parts[0]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
case parts
|
|
28
|
+
when Array
|
|
29
|
+
if (parts.find{|part| not (String === part or Symbol === part)}.nil?)
|
|
30
|
+
parts = parts.map{|part| part.to_s}
|
|
31
|
+
else
|
|
32
|
+
raise "path must be composed of strings or symbols"
|
|
33
|
+
end
|
|
34
|
+
when String
|
|
35
|
+
parts = parts.split(::File::Separator)
|
|
36
|
+
when Symbol
|
|
37
|
+
parts = [parts.to_s]
|
|
38
|
+
when ::File
|
|
39
|
+
parts = parts.path
|
|
40
|
+
parts = parts.split(::File::Separator)
|
|
41
|
+
else
|
|
42
|
+
raise "path must be String, Array of Strings or File"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
parts = parts.map do |part|
|
|
46
|
+
if /^~/ =~ part
|
|
47
|
+
::File::expand_path(part).split(::File::Separator)
|
|
48
|
+
else
|
|
49
|
+
part
|
|
50
|
+
end
|
|
51
|
+
end.flatten
|
|
52
|
+
|
|
53
|
+
return parts
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def repath(segments)
|
|
57
|
+
case segments
|
|
58
|
+
when Array
|
|
59
|
+
return segments.join(::File::Separator)
|
|
60
|
+
when String
|
|
61
|
+
return segments
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
data/lib/valise.rb
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
require 'valise/errors'
|
|
2
|
+
require 'valise/search-root'
|
|
3
|
+
require 'valise/utils'
|
|
4
|
+
require 'valise/stack'
|
|
5
|
+
require 'valise/path-matcher'
|
|
6
|
+
require 'valise/stem-decorator'
|
|
7
|
+
|
|
8
|
+
module Valise
|
|
9
|
+
module Debugging
|
|
10
|
+
def remark
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
class << self
|
|
14
|
+
attr_reader :remark_to
|
|
15
|
+
|
|
16
|
+
def enable(destination)
|
|
17
|
+
@remark_to = destination
|
|
18
|
+
class_eval do
|
|
19
|
+
def remark
|
|
20
|
+
self.class.remark_to.puts(yield)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
class Set
|
|
28
|
+
include Debugging
|
|
29
|
+
include Enumerable
|
|
30
|
+
|
|
31
|
+
class StemmedDefiner
|
|
32
|
+
include Unpath
|
|
33
|
+
def initialize(path, set)
|
|
34
|
+
@segments = unpath(path)
|
|
35
|
+
@target = set
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def rw(name)
|
|
39
|
+
@target.add_search_root(
|
|
40
|
+
StemDecorator.new(@segments, SearchRoot.new(name)))
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def ro(name)
|
|
44
|
+
@target.add_search_root(
|
|
45
|
+
StemDecorator.new(@segments, ReadOnlySearchRoot.new(name)))
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
class Definer
|
|
50
|
+
include Unpath
|
|
51
|
+
def initialize(set)
|
|
52
|
+
@target = set
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def rw(name, path = nil)
|
|
56
|
+
@target.add_search_root(SearchRoot.new(name))
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def ro(name, path = nil)
|
|
60
|
+
@target.add_search_root(ReadOnlySearchRoot.new(name))
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def stemmed(path, &block)
|
|
64
|
+
definer = StemmedDefiner.new(path, @target)
|
|
65
|
+
definer.instance_eval(&block) unless block.nil?
|
|
66
|
+
return definer
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def from_here(rel_path)
|
|
70
|
+
m = /(.*):\d+/.match(caller[0])
|
|
71
|
+
dir = ::File::dirname(::File::expand_path(m[1]))
|
|
72
|
+
|
|
73
|
+
unpath(dir) + unpath(rel_path)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def handle(path, serialization, merge_diff = nil)
|
|
77
|
+
@target.add_handler(unpath(path),
|
|
78
|
+
Valise::Serialization[serialization],
|
|
79
|
+
Valise::MergeDiff[merge_diff])
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def defaults(name=nil, &block)
|
|
83
|
+
loc = DefinedDefaults.define(&block)
|
|
84
|
+
@target.add_search_root(loc)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def initialize
|
|
89
|
+
@search_roots = []
|
|
90
|
+
@merge_diff = PathMatcher.new
|
|
91
|
+
@serialization = PathMatcher.new
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def inspect
|
|
95
|
+
@search_roots.inspect
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def reverse
|
|
99
|
+
set = Set.new
|
|
100
|
+
set.search_roots = @search_roots.reverse
|
|
101
|
+
set
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def define(&block)
|
|
105
|
+
definer = Definer.new(self)
|
|
106
|
+
definer.instance_eval(&block)
|
|
107
|
+
return self
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def self.define(&block)
|
|
111
|
+
return self.new.define(&block)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def prepend_search_root(search_root)
|
|
115
|
+
@search_roots.unshift(search_root)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def add_search_root(search_root)
|
|
119
|
+
@search_roots << search_root
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def add_handler(segments, serialization_class, merge_diff_class)
|
|
123
|
+
@merge_diff[segments] = merge_diff_class unless merge_diff_class.nil?
|
|
124
|
+
@serialization[segments] = serialization_class unless serialization_class.nil?
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def +(other)
|
|
128
|
+
result = self.class.new
|
|
129
|
+
result.search_roots = @search_roots + other.search_roots
|
|
130
|
+
result.merge_handlers(*other.handler_lists)
|
|
131
|
+
result.merge_handlers(*handler_lists)
|
|
132
|
+
return result
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
attr_accessor :search_roots
|
|
136
|
+
protected :search_roots, :search_roots=
|
|
137
|
+
|
|
138
|
+
include Unpath
|
|
139
|
+
def get(path)
|
|
140
|
+
return Stack.new(path, self,
|
|
141
|
+
merge_diff(path),
|
|
142
|
+
serialization(path))
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def merge_diff(path)
|
|
146
|
+
@merge_diff[unpath(path)]
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def serialization(path)
|
|
150
|
+
@serialization[unpath(path)]
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def merge_handlers(merge_diff, serialization)
|
|
154
|
+
@merge_diff.merge!(merge_diff)
|
|
155
|
+
@serialization.merge!(serialization)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def handler_lists
|
|
159
|
+
[@merge_diff, @serialization]
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def find(path)
|
|
163
|
+
return get(path).present.first
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def each(&block)
|
|
167
|
+
@search_roots.each(&block)
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def files
|
|
171
|
+
unless block_given?
|
|
172
|
+
return self.enum_for(:files)
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
visited = {}
|
|
176
|
+
@search_roots.each do |root|
|
|
177
|
+
root.each do |segments|
|
|
178
|
+
unless visited.has_key?(segments)
|
|
179
|
+
item = find(segments)
|
|
180
|
+
visited[segments] = item
|
|
181
|
+
yield(item)
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
return visited
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def not_above(root)
|
|
189
|
+
index = @search_roots.index(root)
|
|
190
|
+
raise RootNotInSet if index.nil?
|
|
191
|
+
set = self.class.new
|
|
192
|
+
set.search_roots = @search_roots[index..-1]
|
|
193
|
+
set
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def below(root)
|
|
197
|
+
index = @search_roots.index(root)
|
|
198
|
+
raise RootNotInSet if index.nil?
|
|
199
|
+
set = self.class.new
|
|
200
|
+
set.search_roots = @search_roots[(index+1)..-1]
|
|
201
|
+
set
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def [](index)
|
|
205
|
+
return @search_roots[index]
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def populate(to = self)
|
|
209
|
+
files do |item|
|
|
210
|
+
contents = item.contents
|
|
211
|
+
to_stack = to.get(item.segments)
|
|
212
|
+
to_stack = yield(to_stack) if block_given?
|
|
213
|
+
target = to_stack.writable.first
|
|
214
|
+
next if target.present?
|
|
215
|
+
target.contents = contents
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
end
|
data/spec/addable.rb
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
require 'valise'
|
|
2
|
+
require 'file-sandbox'
|
|
3
|
+
|
|
4
|
+
describe Valise do
|
|
5
|
+
include FileSandbox
|
|
6
|
+
|
|
7
|
+
before do
|
|
8
|
+
sandbox.new :directory => "etc/conductor"
|
|
9
|
+
sandbox.new :directory => "home/.conductor"
|
|
10
|
+
sandbox.new :directory => ".conductor"
|
|
11
|
+
sandbox.new :directory => "spec"
|
|
12
|
+
sandbox.new :file => "home/.conductor/existed", :with_contents => "TEST"
|
|
13
|
+
one = Valise::Set.define do
|
|
14
|
+
handle "test", :yaml
|
|
15
|
+
rw ".conductor"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
two = Valise::Set.define do
|
|
19
|
+
handle "test", nil, :hash_merge
|
|
20
|
+
defaults do
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
@sum = one + two
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
it "should be addable" do
|
|
29
|
+
@sum.should be_an_instance_of(Valise::Set)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "should add in order" do
|
|
33
|
+
@sum[0].should be_an_instance_of(Valise::SearchRoot)
|
|
34
|
+
@sum[1].should be_an_instance_of(Valise::DefinedDefaults)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it "should combine file handlers" do
|
|
38
|
+
@sum.merge_diff("test").should == Valise::MergeDiff::HashMerge
|
|
39
|
+
@sum.serialization("test").should == Valise::Serialization::YAML
|
|
40
|
+
end
|
|
41
|
+
end
|