soup 1.0.3 → 1.0.4
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/Rakefile +2 -2
- data/lib/soup/backends/file_backend.rb +11 -7
- data/test/file_backend_test.rb +46 -40
- data/test/multi_soup_backend_test.rb +55 -58
- data/test/snip_test.rb +27 -30
- data/test/soup_test.rb +51 -54
- data/test/test_helper.rb +8 -3
- metadata +5 -5
data/Rakefile
CHANGED
@@ -21,7 +21,7 @@ spec = Gem::Specification.new do |s|
|
|
21
21
|
|
22
22
|
# Change these as appropriate
|
23
23
|
s.name = "soup"
|
24
|
-
s.version = "1.0.
|
24
|
+
s.version = "1.0.4"
|
25
25
|
s.summary = "A super-simple data store"
|
26
26
|
s.author = "James Adam"
|
27
27
|
s.email = "james@lazyatom.com"
|
@@ -41,7 +41,7 @@ spec = Gem::Specification.new do |s|
|
|
41
41
|
# s.add_dependency("some_other_gem", "~> 0.1.0")
|
42
42
|
|
43
43
|
# If your tests use any gems, include them here
|
44
|
-
s.add_development_dependency("
|
44
|
+
s.add_development_dependency("kintama")
|
45
45
|
end
|
46
46
|
|
47
47
|
# This task actually builds the gem. We also regenerate a static
|
@@ -10,12 +10,12 @@ class Soup
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def names
|
13
|
-
Dir[
|
13
|
+
Dir[File.join(@base_path, "*")].map { |s| File.basename(s).split(".").first }
|
14
14
|
end
|
15
15
|
|
16
16
|
def load_snip(name)
|
17
|
-
path =
|
18
|
-
if
|
17
|
+
path = Dir[File.join(@base_path, "*")].find { |s| File.basename(s).split(".").first == name }
|
18
|
+
if path
|
19
19
|
file = File.new(path)
|
20
20
|
data = file.read
|
21
21
|
default_attributes = {:name => name, :updated_at => file.mtime, :created_at => file.mtime}
|
@@ -27,6 +27,8 @@ class Soup
|
|
27
27
|
attributes = default_attributes
|
28
28
|
end
|
29
29
|
attributes.update(:content => content) if content && content.length > 0
|
30
|
+
extension = File.extname(path).gsub(/^\./, '')
|
31
|
+
attributes.update(:extension => extension) if extension != "snip"
|
30
32
|
Snip.new(attributes, self)
|
31
33
|
else
|
32
34
|
nil
|
@@ -34,7 +36,7 @@ class Soup
|
|
34
36
|
end
|
35
37
|
|
36
38
|
def save_snip(attributes)
|
37
|
-
File.open(path_for(attributes[:name]), 'w') do |f|
|
39
|
+
File.open(path_for(attributes[:name], attributes[:extension]), 'w') do |f|
|
38
40
|
attributes_without_content = attributes.dup
|
39
41
|
f.write attributes_without_content.delete(:content)
|
40
42
|
f.write attributes_without_content.to_yaml.gsub(/^---\s/, "\n") if attributes_without_content.any?
|
@@ -54,12 +56,14 @@ class Soup
|
|
54
56
|
|
55
57
|
private
|
56
58
|
|
57
|
-
def path_for(name)
|
58
|
-
|
59
|
+
def path_for(name, extension=nil)
|
60
|
+
snip_extension = ".snip"
|
61
|
+
snip_extension += ".#{extension}" if extension
|
62
|
+
File.join(@base_path, name + snip_extension)
|
59
63
|
end
|
60
64
|
|
61
65
|
def all_snips
|
62
|
-
Dir[
|
66
|
+
Dir[File.join(@base_path, "*")].map do |key|
|
63
67
|
load_snip(File.basename(key, ".snip"))
|
64
68
|
end
|
65
69
|
end
|
data/test/file_backend_test.rb
CHANGED
@@ -2,15 +2,13 @@ require "test_helper"
|
|
2
2
|
require "ftools"
|
3
3
|
require "time"
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
@soup = Soup.new(Soup::Backends::FileBackend.new(@base_path))
|
10
|
-
end
|
5
|
+
context "The file-based backend" do
|
6
|
+
setup do
|
7
|
+
@soup = Soup.new(Soup::Backends::FileBackend.new(@base_path))
|
8
|
+
end
|
11
9
|
|
12
|
-
|
13
|
-
|
10
|
+
should "parse attributes from lines starting with colon" do
|
11
|
+
write_snip "snip", %{
|
14
12
|
Here is the content
|
15
13
|
of the snip
|
16
14
|
|
@@ -20,49 +18,57 @@ of the snip
|
|
20
18
|
stuff
|
21
19
|
things
|
22
20
|
:blah: yes
|
23
|
-
|
21
|
+
}
|
24
22
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
23
|
+
snip = @soup["snip"]
|
24
|
+
assert_equal "Here is the content\nof the snip", snip.content
|
25
|
+
assert_equal "thing", snip.name
|
26
|
+
end
|
29
27
|
|
30
|
-
|
31
|
-
|
28
|
+
should "write a snip in the simple format" do
|
29
|
+
@soup << {:content => "Here is the content", :name => "dave"}
|
30
|
+
assert_equal %{Here is the content\n\n:name: dave\n}, File.read(path_for("dave"))
|
31
|
+
end
|
32
32
|
|
33
|
-
|
34
|
-
|
33
|
+
should "not require snip attributes to give content" do
|
34
|
+
write_snip "test", "snip content"
|
35
|
+
assert_equal "snip content", @soup["test"].content
|
36
|
+
end
|
35
37
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
38
|
+
should "take updated_at and created_at from file timestamps if not supplied" do
|
39
|
+
@soup << {:name => "snip", :content => "whatever"}
|
40
|
+
time = Time.parse("2010-11-04 14:56")
|
41
|
+
File.utime(time, time, path_for("snip"))
|
42
|
+
end
|
40
43
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
44
|
+
should "take name from filename if not supplied" do
|
45
|
+
write_snip "blahface", "The snip content"
|
46
|
+
assert_equal "blahface", @soup["blahface"].name
|
47
|
+
end
|
45
48
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
49
|
+
should "take updated_at and created_at from file timestamps if not supplied" do
|
50
|
+
@soup << {:name => "snip", :content => "whatever"}
|
51
|
+
time = Time.parse("2010-11-04 14:56")
|
52
|
+
File.utime(time, time, path_for("snip"))
|
50
53
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
54
|
+
snip = @soup["snip"]
|
55
|
+
assert_equal time, snip.updated_at
|
56
|
+
assert_equal time, snip.created_at
|
57
|
+
end
|
55
58
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
+
should "take created_at from attributes if supplied" do
|
60
|
+
created_at = Time.parse("2010-11-04 14:56")
|
61
|
+
@soup << {:name => "snip", :content => "whatever", :created_at => created_at}
|
59
62
|
|
60
|
-
|
61
|
-
|
62
|
-
end
|
63
|
+
snip = @soup["snip"]
|
64
|
+
assert_equal created_at.to_i, snip.created_at.to_i
|
63
65
|
end
|
64
66
|
|
65
|
-
|
67
|
+
should "set the extension attribute if one is present" do
|
68
|
+
File.open(File.join(@base_path, "test_snip.snip.markdown"), "w") { |f| f.write "This is the content" }
|
69
|
+
snip = @soup["test_snip"]
|
70
|
+
assert_equal "markdown", snip.extension
|
71
|
+
end
|
66
72
|
|
67
73
|
def path_for(name)
|
68
74
|
File.join(@base_path, name + ".snip")
|
@@ -1,76 +1,73 @@
|
|
1
1
|
require "test_helper"
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
@soup = Soup.new(multi_soup_backend)
|
13
|
-
end
|
14
|
-
|
15
|
-
teardown do
|
16
|
-
FileUtils.rm_rf(@base_path)
|
17
|
-
end
|
3
|
+
context "A Soup with multiple backends" do
|
4
|
+
setup do
|
5
|
+
@basic_soup_backend_one = Soup::Backends::YAMLBackend.new(File.join(@base_path, "soup_one"))
|
6
|
+
@basic_soup_backend_two = Soup::Backends::FileBackend.new(File.join(@base_path, "soup_two"))
|
7
|
+
@soup_one = Soup.new(@basic_soup_backend_one)
|
8
|
+
@soup_two = Soup.new(@basic_soup_backend_two)
|
9
|
+
multi_soup_backend = Soup::Backends::MultiSoup.new(@basic_soup_backend_one, @basic_soup_backend_two)
|
10
|
+
@soup = Soup.new(multi_soup_backend)
|
11
|
+
end
|
18
12
|
|
19
|
-
|
20
|
-
|
21
|
-
|
13
|
+
teardown do
|
14
|
+
FileUtils.rm_rf(@base_path)
|
15
|
+
end
|
22
16
|
|
23
|
-
|
24
|
-
|
25
|
-
|
17
|
+
should "return nil when the requested snip is not present in any backend" do
|
18
|
+
assert_nil @soup["snip"]
|
19
|
+
end
|
26
20
|
|
27
|
-
|
28
|
-
|
29
|
-
|
21
|
+
should "return a snip if any backend contains it" do
|
22
|
+
@soup_one << {:name => "snip", :body => "hello"}
|
23
|
+
assert_equal "hello", @soup["snip"].body
|
30
24
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
@soup_two << {:name => "snip", :body => "from soup two"}
|
35
|
-
end
|
25
|
+
@soup_two << {:name => "other_snip", :body => "hi!"}
|
26
|
+
assert_equal "hi!", @soup["other_snip"].body
|
27
|
+
end
|
36
28
|
|
37
|
-
|
38
|
-
|
39
|
-
|
29
|
+
context "when snips of the same name exist in multiple backends" do
|
30
|
+
setup do
|
31
|
+
@soup_one << {:name => "snip", :body => "from soup one"}
|
32
|
+
@soup_two << {:name => "snip", :body => "from soup two"}
|
40
33
|
end
|
41
34
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
end
|
35
|
+
should "load the snip from the backend with the higher precidence" do
|
36
|
+
assert_equal "from soup one", @soup["snip"].body
|
37
|
+
end
|
38
|
+
end
|
47
39
|
|
48
|
-
|
49
|
-
|
50
|
-
|
40
|
+
context "when snips with a certain attribute exist in multiple backends" do
|
41
|
+
setup do
|
42
|
+
@soup_one << {:name => "snip1", :active => true}
|
43
|
+
@soup_two << {:name => "snip2", :active => true}
|
51
44
|
end
|
52
45
|
|
53
|
-
should "
|
54
|
-
|
55
|
-
@soup.destroy("snip")
|
56
|
-
assert_nil @soup["snip"]
|
46
|
+
should "find matching snips from all backends" do
|
47
|
+
assert_equal 2, @soup[:active => true].length
|
57
48
|
end
|
49
|
+
end
|
50
|
+
|
51
|
+
should "save snips" do
|
52
|
+
@soup << {:name => "snip", :body => "bad snip"}
|
53
|
+
@soup.destroy("snip")
|
54
|
+
assert_nil @soup["snip"]
|
55
|
+
end
|
58
56
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
57
|
+
context "when a backend is read-only" do
|
58
|
+
setup do
|
59
|
+
readonly_backend = Soup::Backends::ReadOnly.new(@basic_soup_backend_one)
|
60
|
+
@soup_one = Soup.new(readonly_backend)
|
61
|
+
@soup_two = Soup.new(@basic_soup_backend_two)
|
62
|
+
multi_soup_backend = Soup::Backends::MultiSoup.new(readonly_backend, @basic_soup_backend_two)
|
63
|
+
@soup = Soup.new(multi_soup_backend)
|
64
|
+
end
|
67
65
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
end
|
66
|
+
should "store snips in the writeable backend" do
|
67
|
+
@soup << {:name => "snip", :body => "hello"}
|
68
|
+
assert_equal "hello", @soup["snip"].body
|
69
|
+
assert_nil @soup_one["snip"]
|
70
|
+
assert_not_nil @soup_two["snip"]
|
74
71
|
end
|
75
72
|
end
|
76
73
|
end
|
data/test/snip_test.rb
CHANGED
@@ -1,41 +1,38 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
end
|
3
|
+
context "A snip" do
|
4
|
+
setup do
|
5
|
+
@snip = Soup::Snip.new({:name => "james", :content => "is awesome"}, nil)
|
6
|
+
end
|
8
7
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
8
|
+
should "be equal to another snip with the same attributes" do
|
9
|
+
other_snip = Soup::Snip.new({:name => "james", :content => "is awesome"}, nil)
|
10
|
+
assert other_snip == @snip
|
11
|
+
end
|
13
12
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
13
|
+
should "not be equal to another snip with differing attributes" do
|
14
|
+
other_snip = Soup::Snip.new({:name => "james", :content => "is really awesome"}, nil)
|
15
|
+
assert other_snip != @snip
|
16
|
+
end
|
18
17
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
18
|
+
should "be comparable in arrays" do
|
19
|
+
other_snip = Soup::Snip.new({:name => "james", :content => "is awesome"}, nil)
|
20
|
+
assert [@snip] == [other_snip]
|
21
|
+
end
|
23
22
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
end
|
23
|
+
context "loaded from the soup" do
|
24
|
+
setup do
|
25
|
+
backend = Soup::Backends::FileBackend.new(@base_path)
|
26
|
+
@soup = Soup.new(backend)
|
27
|
+
end
|
30
28
|
|
31
|
-
|
32
|
-
|
33
|
-
|
29
|
+
teardown do
|
30
|
+
FileUtils.rm_rf(@base_path)
|
31
|
+
end
|
34
32
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
end
|
33
|
+
should "ignore empty content when comparing" do
|
34
|
+
@soup << {:name => 'test'}
|
35
|
+
assert_equal Soup::Snip.new({:name => 'test'}, nil), @soup['test']
|
39
36
|
end
|
40
37
|
end
|
41
38
|
end
|
data/test/soup_test.rb
CHANGED
@@ -1,71 +1,68 @@
|
|
1
1
|
require "test_helper"
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
teardown do
|
18
|
-
FileUtils.rm_rf(base_path)
|
19
|
-
end
|
20
|
-
yield
|
3
|
+
def each_backend(&block)
|
4
|
+
base_path = File.join(File.dirname(__FILE__), *%w[.. tmp soup])
|
5
|
+
backends = [
|
6
|
+
yaml_backend = Soup::Backends::YAMLBackend.new(base_path),
|
7
|
+
file_backend = Soup::Backends::FileBackend.new(base_path),
|
8
|
+
Soup::Backends::MultiSoup.new(yaml_backend)
|
9
|
+
]
|
10
|
+
backends.each do |backend|
|
11
|
+
describe backend.class do
|
12
|
+
setup do
|
13
|
+
@soup = Soup.new(backend)
|
14
|
+
end
|
15
|
+
teardown do
|
16
|
+
FileUtils.rm_rf(base_path)
|
21
17
|
end
|
18
|
+
instance_eval(&block)
|
22
19
|
end
|
23
20
|
end
|
21
|
+
end
|
24
22
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
23
|
+
each_backend do
|
24
|
+
should "be able to store content" do
|
25
|
+
@soup << {:name => 'test', :content => "I like stuff, and things"}
|
26
|
+
assert_equal "I like stuff, and things", @soup['test'].content
|
27
|
+
end
|
30
28
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
29
|
+
should "return a snip when storing content" do
|
30
|
+
snip = @soup << {:name => 'test', :content => "I like stuff, and things"}
|
31
|
+
assert_equal "I like stuff, and things", snip.content
|
32
|
+
end
|
35
33
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
34
|
+
context "when sieving the soup" do
|
35
|
+
setup do
|
36
|
+
@james = @soup << {:name => 'james', :spirit_guide => 'fox', :colour => 'blue', :powers => 'yes'}
|
37
|
+
@murray = @soup << {:name => 'murray', :spirit_guide => 'chaffinch', :colour => 'red', :powers => 'yes'}
|
38
|
+
end
|
41
39
|
|
42
|
-
|
43
|
-
|
44
|
-
|
40
|
+
should "find snips by name if the parameter is a string" do
|
41
|
+
assert_equal @james, @soup['james']
|
42
|
+
end
|
45
43
|
|
46
|
-
|
47
|
-
|
48
|
-
|
44
|
+
should "find snips using exact matching of keys and values if the parameter is a hash" do
|
45
|
+
assert_equal @murray, @soup[:name => 'murray']
|
46
|
+
end
|
49
47
|
|
50
|
-
|
51
|
-
|
52
|
-
|
48
|
+
should "match using all parameters" do
|
49
|
+
assert_equal [@murray], @soup[:powers => 'yes', :colour => 'red']
|
50
|
+
end
|
53
51
|
|
54
|
-
|
55
|
-
|
56
|
-
|
52
|
+
should "return an array if more than one snip matches" do
|
53
|
+
assert_equal [@james, @murray], @soup[:powers => 'yes']
|
54
|
+
end
|
57
55
|
|
58
|
-
|
59
|
-
|
60
|
-
end
|
56
|
+
should "return an empty array if no matching snips exist" do
|
57
|
+
assert_equal [], @soup[:powers => 'maybe']
|
61
58
|
end
|
59
|
+
end
|
62
60
|
|
63
|
-
|
64
|
-
|
65
|
-
|
61
|
+
should "allow deletion of snips" do
|
62
|
+
snip = @soup << {:name => 'test', :content => 'content'}
|
63
|
+
assert_equal snip, @soup['test']
|
66
64
|
|
67
|
-
|
68
|
-
|
69
|
-
end
|
65
|
+
@soup['test'].destroy
|
66
|
+
assert @soup['test'].nil?
|
70
67
|
end
|
71
|
-
end
|
68
|
+
end
|
data/test/test_helper.rb
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
-
require '
|
2
|
-
|
3
|
-
require '
|
1
|
+
# require 'rubygems'
|
2
|
+
$LOAD_PATH.unshift "/Users/james/Code/lazyatom/jtest/lib"
|
3
|
+
require 'kintama'
|
4
|
+
require 'soup'
|
5
|
+
|
6
|
+
Kintama.setup do
|
7
|
+
@base_path = File.join(File.dirname(__FILE__), *%w[.. tmp soup])
|
8
|
+
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: soup
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 31
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 1
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 1.0.
|
9
|
+
- 4
|
10
|
+
version: 1.0.4
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- James Adam
|
@@ -15,11 +15,11 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-
|
18
|
+
date: 2011-03-01 00:00:00 +00:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
22
|
-
name:
|
22
|
+
name: kintama
|
23
23
|
prerelease: false
|
24
24
|
requirement: &id001 !ruby/object:Gem::Requirement
|
25
25
|
none: false
|