soup 0.2.1 → 0.9.9
Sign up to get free protection for your applications and to get access to all the features.
- data/Manifest +1 -7
- data/Rakefile +115 -0
- data/lib/soup.rb +101 -75
- data/lib/soup/empty_class.rb +1 -1
- data/lib/soup/snip.rb +17 -127
- data/test/soup_test.rb +54 -0
- metadata +48 -53
- data/lib/soup/tuples/active_record_tuple.rb +0 -48
- data/lib/soup/tuples/data_mapper_tuple.rb +0 -50
- data/lib/soup/tuples/sequel_tuple.rb +0 -36
- data/soup.gemspec +0 -68
- data/spec/snip_spec.rb +0 -102
- data/spec/soup_spec.rb +0 -148
- data/spec/spec_helper.rb +0 -8
- data/spec/spec_runner.rb +0 -12
data/Manifest
CHANGED
@@ -1,12 +1,6 @@
|
|
1
1
|
lib/soup/empty_class.rb
|
2
2
|
lib/soup/snip.rb
|
3
|
-
lib/soup/tuples/active_record_tuple.rb
|
4
|
-
lib/soup/tuples/data_mapper_tuple.rb
|
5
|
-
lib/soup/tuples/sequel_tuple.rb
|
6
3
|
lib/soup.rb
|
7
4
|
Manifest
|
8
5
|
README
|
9
|
-
|
10
|
-
spec/soup_spec.rb
|
11
|
-
spec/spec_helper.rb
|
12
|
-
spec/spec_runner.rb
|
6
|
+
test/soup_test.rb
|
data/Rakefile
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "rake/gempackagetask"
|
3
|
+
require "rake/rdoctask"
|
4
|
+
|
5
|
+
task :default => :test
|
6
|
+
|
7
|
+
require "rake/testtask"
|
8
|
+
Rake::TestTask.new do |t|
|
9
|
+
t.libs << "test"
|
10
|
+
t.test_files = FileList["test/**/*_test.rb"]
|
11
|
+
t.verbose = true
|
12
|
+
end
|
13
|
+
|
14
|
+
# This builds the actual gem. For details of what all these options
|
15
|
+
# mean, and other ones you can add, check the documentation here:
|
16
|
+
#
|
17
|
+
# http://rubygems.org/read/chapter/20
|
18
|
+
#
|
19
|
+
spec = Gem::Specification.new do |s|
|
20
|
+
|
21
|
+
# Change these as appropriate
|
22
|
+
s.name = "soup"
|
23
|
+
s.version = "0.9.9"
|
24
|
+
s.summary = "A super-simple data store"
|
25
|
+
s.author = "James Adam"
|
26
|
+
s.email = "james@lazyatom.com"
|
27
|
+
s.homepage = "http://lazyatom.com"
|
28
|
+
|
29
|
+
s.has_rdoc = true
|
30
|
+
s.extra_rdoc_files = %w(README)
|
31
|
+
s.rdoc_options = %w(--main README)
|
32
|
+
|
33
|
+
# Add any extra files to include in the gem
|
34
|
+
s.files = %w(Manifest Rakefile README) + Dir.glob("{test,lib}/**/*")
|
35
|
+
|
36
|
+
s.require_paths = ["lib"]
|
37
|
+
|
38
|
+
# If you want to depend on other gems, add them here, along with any
|
39
|
+
# relevant versions
|
40
|
+
# s.add_dependency("some_other_gem", "~> 0.1.0")
|
41
|
+
|
42
|
+
# If your tests use any gems, include them here
|
43
|
+
# s.add_development_dependency("mocha")
|
44
|
+
|
45
|
+
# If you want to publish automatically to rubyforge, you'll may need
|
46
|
+
# to tweak this, and the publishing task below too.
|
47
|
+
s.rubyforge_project = "soup"
|
48
|
+
end
|
49
|
+
|
50
|
+
# This task actually builds the gem. We also regenerate a static
|
51
|
+
# .gemspec file, which is useful if something (i.e. GitHub) will
|
52
|
+
# be automatically building a gem for this project. If you're not
|
53
|
+
# using GitHub, edit as appropriate.
|
54
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
55
|
+
pkg.gem_spec = spec
|
56
|
+
|
57
|
+
# Generate the gemspec file for github.
|
58
|
+
file = File.dirname(__FILE__) + "/#{spec.name}.gemspec"
|
59
|
+
File.open(file, "w") {|f| f << spec.to_ruby }
|
60
|
+
end
|
61
|
+
|
62
|
+
# Generate documentation
|
63
|
+
Rake::RDocTask.new do |rd|
|
64
|
+
rd.main = "README"
|
65
|
+
rd.rdoc_files.include("README", "lib/**/*.rb")
|
66
|
+
rd.rdoc_dir = "rdoc"
|
67
|
+
end
|
68
|
+
|
69
|
+
desc 'Clear out RDoc and generated packages'
|
70
|
+
task :clean => [:clobber_rdoc, :clobber_package] do
|
71
|
+
rm "#{spec.name}.gemspec"
|
72
|
+
end
|
73
|
+
|
74
|
+
# If you want to publish to RubyForge automatically, here's a simple
|
75
|
+
# task to help do that. If you don't, just get rid of this.
|
76
|
+
# Be sure to set up your Rubyforge account details with the Rubyforge
|
77
|
+
# gem; you'll need to run `rubyforge setup` and `rubyforge config` at
|
78
|
+
# the very least.
|
79
|
+
begin
|
80
|
+
require "rake/contrib/sshpublisher"
|
81
|
+
namespace :rubyforge do
|
82
|
+
|
83
|
+
desc "Release gem and RDoc documentation to RubyForge"
|
84
|
+
task :release => ["rubyforge:release:gem", "rubyforge:release:docs"]
|
85
|
+
|
86
|
+
namespace :release do
|
87
|
+
desc "Release a new version of this gem"
|
88
|
+
task :gem => [:package] do
|
89
|
+
require 'rubyforge'
|
90
|
+
rubyforge = RubyForge.new
|
91
|
+
rubyforge.configure
|
92
|
+
rubyforge.login
|
93
|
+
rubyforge.userconfig['release_notes'] = spec.summary
|
94
|
+
path_to_gem = File.join(File.dirname(__FILE__), "pkg", "#{spec.name}-#{spec.version}.gem")
|
95
|
+
puts "Publishing #{spec.name}-#{spec.version.to_s} to Rubyforge..."
|
96
|
+
rubyforge.add_release(spec.rubyforge_project, spec.name, spec.version.to_s, path_to_gem)
|
97
|
+
end
|
98
|
+
|
99
|
+
desc "Publish RDoc to RubyForge."
|
100
|
+
task :docs => [:rdoc] do
|
101
|
+
config = YAML.load(
|
102
|
+
File.read(File.expand_path('~/.rubyforge/user-config.yml'))
|
103
|
+
)
|
104
|
+
|
105
|
+
host = "#{config['username']}@rubyforge.org"
|
106
|
+
remote_dir = "/var/www/gforge-projects/soup/" # Should be the same as the rubyforge project name
|
107
|
+
local_dir = 'rdoc'
|
108
|
+
|
109
|
+
Rake::SshDirPublisher.new(host, remote_dir, local_dir).upload
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
rescue LoadError
|
114
|
+
puts "Rake SshDirPublisher is unavailable or your rubyforge environment is not configured."
|
115
|
+
end
|
data/lib/soup.rb
CHANGED
@@ -2,95 +2,121 @@
|
|
2
2
|
$LOAD_PATH.unshift(File.dirname(__FILE__)).uniq!
|
3
3
|
|
4
4
|
require 'soup/snip'
|
5
|
+
require 'yaml'
|
6
|
+
require 'fileutils'
|
5
7
|
|
6
|
-
|
7
|
-
VERSION = "0.
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
# Call this to set which tuple implementation to use, i.e.
|
26
|
-
#
|
27
|
-
# Soup.flavour = :active_record
|
28
|
-
#
|
29
|
-
def self.flavour=(tuple_implementation)
|
30
|
-
@tuple_implementation = "#{tuple_implementation}_tuple"
|
31
|
-
# We want to reset the tuple class if we re-flavour the soup.
|
32
|
-
@tuple_class = nil
|
33
|
-
end
|
34
|
-
|
35
|
-
def self.tuple_class
|
36
|
-
@tuple_class ||= case (@tuple_implementation || DEFAULT_TUPLE_IMPLEMENTATION)
|
37
|
-
when "active_record_tuple", nil
|
38
|
-
Soup::Tuples::ActiveRecordTuple
|
39
|
-
when "data_mapper_tuple"
|
40
|
-
Soup::Tuples::DataMapperTuple
|
41
|
-
when "sequel_tuple"
|
42
|
-
Soup::Tuples::SequelTuple
|
43
|
-
end
|
8
|
+
class Soup
|
9
|
+
VERSION = "0.9.9"
|
10
|
+
|
11
|
+
# You can access a default soup using this methods.
|
12
|
+
|
13
|
+
def self.default_instance #:nodoc:
|
14
|
+
@@instance ||= new
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.[](*args)
|
18
|
+
default_instance[*args]
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.<<(attributes)
|
22
|
+
default_instance << attributes
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.sieve(*args)
|
26
|
+
default_instance.sieve(*args)
|
44
27
|
end
|
45
|
-
|
28
|
+
|
29
|
+
def self.destroy(*args)
|
30
|
+
default_instance.destroy(*args)
|
31
|
+
end
|
32
|
+
|
33
|
+
attr_reader :base_path
|
34
|
+
|
46
35
|
# Get the soup ready!
|
47
|
-
def
|
48
|
-
|
49
|
-
|
36
|
+
def initialize(base_path=nil)
|
37
|
+
@base_path = base_path || "soup"
|
38
|
+
FileUtils.mkdir_p(base_path)
|
50
39
|
end
|
51
|
-
|
40
|
+
|
52
41
|
# The main interface
|
53
42
|
# ==================
|
54
|
-
|
55
|
-
#
|
56
|
-
#
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
Snip.sieve(*args)
|
43
|
+
|
44
|
+
# A shorthand for #sieve, with the addition that only a name may be
|
45
|
+
# supplied (i.e. Soup['my snip'])
|
46
|
+
def [](conditions)
|
47
|
+
conditions = {:name => conditions} unless conditions.respond_to?(:keys)
|
48
|
+
sieve(conditions)
|
61
49
|
end
|
62
|
-
|
50
|
+
|
63
51
|
# Puts some data into the soup, and returns an object that contains
|
64
52
|
# that data. The object should respond to accessing and setting its
|
65
53
|
# attributes as if they were defined using attr_accessor on the object's
|
66
54
|
# class.
|
67
|
-
def
|
68
|
-
|
69
|
-
|
70
|
-
s
|
55
|
+
def <<(attributes)
|
56
|
+
save_snip(attributes)
|
57
|
+
Snip.new(attributes, self)
|
71
58
|
end
|
72
|
-
|
73
|
-
#
|
74
|
-
|
75
|
-
|
76
|
-
|
59
|
+
|
60
|
+
# Finds bits in the soup that make the given attribute hash.
|
61
|
+
# This method should eventually be delegated to the underlying persistence
|
62
|
+
# layers (i.e. Snips and Tuples, or another document database). The expected
|
63
|
+
# behaviour is
|
64
|
+
def sieve(conditions)
|
65
|
+
conditions = symbolize_keys(conditions)
|
66
|
+
if conditions.keys == [:name]
|
67
|
+
load_snip(conditions[:name])
|
77
68
|
else
|
78
|
-
|
69
|
+
all_snips.select do |s|
|
70
|
+
conditions.inject(true) do |matches, (key, value)|
|
71
|
+
matches && (s.__send__(key) == value)
|
72
|
+
end
|
73
|
+
end
|
79
74
|
end
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
def
|
87
|
-
|
88
|
-
|
89
|
-
snip = Snip.find(id) rescue nil
|
90
|
-
snips[snip.id] = snip if snip
|
75
|
+
end
|
76
|
+
|
77
|
+
def destroy(name)
|
78
|
+
File.delete(path_for(name))
|
79
|
+
end
|
80
|
+
|
81
|
+
def all_snips
|
82
|
+
Dir[path_for("*")].map do |path|
|
83
|
+
load_snip(File.basename(path, ".yml"))
|
91
84
|
end
|
92
|
-
|
93
|
-
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def save_snip(attributes)
|
90
|
+
attributes = symbolize_keys(attributes)
|
91
|
+
File.open(path_for(attributes[:name]), 'w') do |f|
|
92
|
+
content = attributes.delete(:content)
|
93
|
+
f.write content
|
94
|
+
f.write attributes.to_yaml.gsub(/^---\s/, attribute_token)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def load_snip(name)
|
99
|
+
path = path_for(name)
|
100
|
+
if File.exist?(path)
|
101
|
+
file = File.read(path)
|
102
|
+
attribute_start = file.index(attribute_token)
|
103
|
+
content = file.slice(0...attribute_start)
|
104
|
+
attributes = YAML.load(file.slice(attribute_start..-1)).merge(:content => content)
|
105
|
+
Snip.new(attributes, self)
|
106
|
+
else
|
107
|
+
nil
|
94
108
|
end
|
95
109
|
end
|
96
|
-
|
110
|
+
|
111
|
+
def path_for(filename)
|
112
|
+
File.join(base_path, filename + ".yml")
|
113
|
+
end
|
114
|
+
|
115
|
+
def attribute_token
|
116
|
+
"--- # Soup attributes"
|
117
|
+
end
|
118
|
+
|
119
|
+
def symbolize_keys(hash)
|
120
|
+
hash.inject({}) { |h,(k,v)| h[k.to_sym] = v; h }
|
121
|
+
end
|
122
|
+
end
|
data/lib/soup/empty_class.rb
CHANGED
data/lib/soup/snip.rb
CHANGED
@@ -1,148 +1,38 @@
|
|
1
|
-
require 'rubygems'
|
2
1
|
require 'soup/empty_class'
|
3
2
|
|
4
|
-
# methods called on Tuple:
|
5
|
-
# Tuple.for_snip(id)
|
6
|
-
# Tuple.find_matching(tuple_name, tuple_value_conditions)
|
7
|
-
# Tuple.find_matching_hash(key => value, key2 => value2, ...)
|
8
|
-
# Tuple.next_snip_id
|
9
|
-
# Tuple#save
|
10
|
-
# Tuple#name
|
11
|
-
# Tuple#value
|
12
|
-
# Tuple#destroy
|
13
|
-
|
14
3
|
class Snip < Soup::EmptyClass
|
4
|
+
attr_reader :attributes
|
15
5
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
# Snip.sieve(:created_at, "> '2007-01-01'")
|
20
|
-
#
|
21
|
-
# should return all Snips who have a 'created_at' value greater than '2007-01-01'.
|
22
|
-
#
|
23
|
-
def self.sieve(*args)
|
24
|
-
if args.length > 1
|
25
|
-
name = args[0]
|
26
|
-
tuple_value_conditions = args[1]
|
27
|
-
matching_tuples = Soup.tuple_class.find_matching(name, tuple_value_conditions)
|
28
|
-
matching_tuples.map { |t| t.snip_id }.uniq.map { |snip_id| find(snip_id) }
|
29
|
-
else
|
30
|
-
pairs = args[0].inject([]) { |ary, (name, value)| ary << [name, value] }
|
31
|
-
matching_tuples = pairs.map { |(name, value)| Soup.tuple_class.find_matching(name, "='#{value}'") }.flatten
|
32
|
-
snips = matching_tuples.map { |t| t.snip_id }.uniq.map { |snip_id| find(snip_id) }
|
33
|
-
snips.reject { |s| pairs.map { |(name, value)| s.get_value(name) == value }.include?(false) }
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
# Returns the snip with the given ID (i.e. the collection of all tuples
|
38
|
-
# with the matching snip_id, gathered into a magical snip.)
|
39
|
-
#
|
40
|
-
def self.find(id)
|
41
|
-
raise "not found" unless (tuples = Soup.tuple_class.for_snip(id)).any?
|
42
|
-
snip = Snip.new(:__id => id)
|
43
|
-
snip.replace_tuples(tuples)
|
44
|
-
snip
|
45
|
-
end
|
46
|
-
|
47
|
-
attr_reader :tuples
|
48
|
-
|
49
|
-
def initialize(attributes = {})
|
50
|
-
set_id(attributes.delete(:__id))
|
51
|
-
@tuples = {}
|
52
|
-
attributes.each { |name, value| set_value(name, value) }
|
53
|
-
end
|
54
|
-
|
55
|
-
def respond_to?(method)
|
56
|
-
attributes.keys.include?(method.to_s)
|
6
|
+
def initialize(attributes = {}, soup = Soup)
|
7
|
+
@attributes = attributes
|
8
|
+
@soup = soup
|
57
9
|
end
|
58
|
-
|
10
|
+
|
59
11
|
def save
|
60
|
-
|
61
|
-
set_id_if_necessary
|
62
|
-
each_tuple { |t| t.save }
|
12
|
+
@soup << @attributes
|
63
13
|
self
|
64
14
|
end
|
65
|
-
|
15
|
+
|
66
16
|
def destroy
|
67
|
-
|
68
|
-
end
|
69
|
-
|
70
|
-
def reload
|
71
|
-
return self unless self.id
|
72
|
-
replace_tuples(Soup.tuple_class.for_snip(id))
|
17
|
+
@soup.destroy(self.name)
|
73
18
|
self
|
74
19
|
end
|
75
|
-
|
76
|
-
def attributes
|
77
|
-
@tuples.inject({}) { |h, (name, t)| h[name] = t.value; h }
|
78
|
-
end
|
79
20
|
|
80
|
-
def replace_tuples(new_tuples)
|
81
|
-
@tuples.clear
|
82
|
-
new_tuples.each { |tuple| @tuples[tuple.name] = tuple }
|
83
|
-
end
|
84
|
-
|
85
|
-
def to_s
|
86
|
-
"<Snip id:#{self.id || "unset"} #{tuples_as_string}>"
|
87
|
-
end
|
88
|
-
|
89
21
|
def inspect
|
90
|
-
"<Snip
|
22
|
+
"<Snip name:#{self.name}>"
|
91
23
|
end
|
92
|
-
|
93
|
-
def
|
94
|
-
attributes.
|
24
|
+
|
25
|
+
def respond_to?(method)
|
26
|
+
@attributes.keys.include?(method.to_s)
|
95
27
|
end
|
96
|
-
|
28
|
+
|
97
29
|
def method_missing(method, *args)
|
98
30
|
value = args.length > 1 ? args : args.first
|
99
|
-
if method.to_s =~ /(.*)=\Z/
|
100
|
-
|
101
|
-
else
|
102
|
-
get_value(method.to_s)
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
def id #:nodoc: why is ID special?
|
107
|
-
@id
|
108
|
-
end
|
109
|
-
|
110
|
-
def get_value(name)
|
111
|
-
@tuples[name.to_s] ? @tuples[name.to_s].value : nil
|
112
|
-
end
|
113
|
-
|
114
|
-
def set_value(name, value)
|
115
|
-
tuple = @tuples[name.to_s]
|
116
|
-
if tuple
|
117
|
-
tuple.value = value
|
31
|
+
if method.to_s =~ /(.*)=\Z/
|
32
|
+
@attributes[$1.to_sym] = value
|
118
33
|
else
|
119
|
-
attributes
|
120
|
-
tuple = @tuples[name.to_s] = Soup.tuple_class.new(attributes)
|
34
|
+
@attributes[method]
|
121
35
|
end
|
122
|
-
tuple.value
|
123
|
-
end
|
124
|
-
|
125
|
-
|
126
|
-
private
|
127
|
-
|
128
|
-
def each_tuple
|
129
|
-
@tuples.values.each { |tuple| yield tuple }
|
130
|
-
end
|
131
|
-
|
132
|
-
def set_id(id)
|
133
|
-
@id = id
|
134
|
-
self
|
135
36
|
end
|
136
37
|
|
137
|
-
|
138
|
-
if self.id.nil?
|
139
|
-
set_id(Soup.tuple_class.next_snip_id)
|
140
|
-
@tuples.values.each { |tuple| tuple.snip_id = self.id }
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
def tuples_as_string
|
145
|
-
@tuples.inject("") { |hash, (name, tuple)| hash += " #{name}:'#{tuple.value}'" }.strip
|
146
|
-
end
|
147
|
-
|
148
|
-
end
|
38
|
+
end
|
data/test/soup_test.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'shoulda'
|
3
|
+
require 'soup'
|
4
|
+
|
5
|
+
class SoupTest < Test::Unit::TestCase
|
6
|
+
|
7
|
+
context "Given a soup" do
|
8
|
+
setup do
|
9
|
+
@soup = Soup.new(File.join(File.dirname(__FILE__), *%w[.. tmp soup]))
|
10
|
+
end
|
11
|
+
|
12
|
+
should "be able to store content" do
|
13
|
+
@soup << {:name => 'test', :content => "I like stuff, and things"}
|
14
|
+
assert_equal "I like stuff, and things", @soup['test'].content
|
15
|
+
end
|
16
|
+
|
17
|
+
context "when sieving the soup" do
|
18
|
+
setup do
|
19
|
+
@james = @soup << {:name => 'james', :spirit_guide => 'fox', :colour => 'blue', :powers => 'yes'}
|
20
|
+
@murray = @soup << {:name => 'murray', :spirit_guide => 'chaffinch', :colour => 'red', :powers => 'yes'}
|
21
|
+
end
|
22
|
+
|
23
|
+
should "find snips by name if the parameter is a string" do
|
24
|
+
assert_equal @james, @soup['james']
|
25
|
+
end
|
26
|
+
|
27
|
+
should "find snips using exact matching of keys and values if the parameter is a hash" do
|
28
|
+
assert_equal @murray, @soup[:name => 'murray']
|
29
|
+
end
|
30
|
+
|
31
|
+
should "match using all parameters" do
|
32
|
+
assert_equal @james, @soup[:powers => 'yes', :colour => 'red']
|
33
|
+
end
|
34
|
+
|
35
|
+
should "return an array if more than one snip matches" do
|
36
|
+
assert_equal [@james, @murray], @soup[:powers => 'yes']
|
37
|
+
end
|
38
|
+
|
39
|
+
should "return an empty array if no matching snips exist" do
|
40
|
+
assert_equal [], @soup[:powers => 'maybe']
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context "when deleting snips" do
|
45
|
+
should "allow deletion of snips" do
|
46
|
+
snip = @soup << {:name => 'test', :content => 'content'}
|
47
|
+
assert_equal snip, @soup['test']
|
48
|
+
|
49
|
+
@soup['test'].destroy
|
50
|
+
assert @soup['test'].nil?
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
metadata
CHANGED
@@ -1,67 +1,62 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
|
-
rubygems_version: 0.9.4
|
3
|
-
specification_version: 1
|
4
2
|
name: soup
|
5
3
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.
|
7
|
-
date: 2008-04-17 00:00:00 +01:00
|
8
|
-
summary: Soup is a bit of everything, summoned from nothing. Soup is like an imaginary friend - comforting,
|
9
|
-
require_paths:
|
10
|
-
- lib
|
11
|
-
email:
|
12
|
-
- james@lazyatom.com
|
13
|
-
homepage: ""
|
14
|
-
rubyforge_project: soup
|
15
|
-
description: Soup is a bit of everything, summoned from nothing. Soup is like an imaginary friend - comforting,
|
16
|
-
autorequire:
|
17
|
-
default_executable:
|
18
|
-
bindir: bin
|
19
|
-
has_rdoc: true
|
20
|
-
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
21
|
-
requirements:
|
22
|
-
- - ">"
|
23
|
-
- !ruby/object:Gem::Version
|
24
|
-
version: 0.0.0
|
25
|
-
version:
|
4
|
+
version: 0.9.9
|
26
5
|
platform: ruby
|
27
|
-
signing_key:
|
28
|
-
cert_chain:
|
29
|
-
post_install_message:
|
30
6
|
authors:
|
31
7
|
- James Adam
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
- lib/soup/tuples/active_record_tuple.rb
|
36
|
-
- lib/soup/tuples/data_mapper_tuple.rb
|
37
|
-
- lib/soup/tuples/sequel_tuple.rb
|
38
|
-
- lib/soup.rb
|
39
|
-
- Manifest
|
40
|
-
- README
|
41
|
-
- spec/snip_spec.rb
|
42
|
-
- spec/soup_spec.rb
|
43
|
-
- spec/spec_helper.rb
|
44
|
-
- spec/spec_runner.rb
|
45
|
-
- soup.gemspec
|
46
|
-
test_files: []
|
47
|
-
|
48
|
-
rdoc_options: []
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
49
11
|
|
50
|
-
|
12
|
+
date: 2009-10-12 00:00:00 +01:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
51
15
|
|
16
|
+
description:
|
17
|
+
email: james@lazyatom.com
|
52
18
|
executables: []
|
53
19
|
|
54
20
|
extensions: []
|
55
21
|
|
22
|
+
extra_rdoc_files:
|
23
|
+
- README
|
24
|
+
files:
|
25
|
+
- Manifest
|
26
|
+
- Rakefile
|
27
|
+
- README
|
28
|
+
- test/soup_test.rb
|
29
|
+
- lib/soup/empty_class.rb
|
30
|
+
- lib/soup/snip.rb
|
31
|
+
- lib/soup.rb
|
32
|
+
has_rdoc: true
|
33
|
+
homepage: http://lazyatom.com
|
34
|
+
licenses: []
|
35
|
+
|
36
|
+
post_install_message:
|
37
|
+
rdoc_options:
|
38
|
+
- --main
|
39
|
+
- README
|
40
|
+
require_paths:
|
41
|
+
- lib
|
42
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: "0"
|
47
|
+
version:
|
48
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: "0"
|
53
|
+
version:
|
56
54
|
requirements: []
|
57
55
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
- !ruby/object:Gem::Version
|
66
|
-
version: 2.0.2
|
67
|
-
version:
|
56
|
+
rubyforge_project: soup
|
57
|
+
rubygems_version: 1.3.3
|
58
|
+
signing_key:
|
59
|
+
specification_version: 3
|
60
|
+
summary: A super-simple data store
|
61
|
+
test_files: []
|
62
|
+
|
@@ -1,48 +0,0 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
gem 'activerecord', '>=2.0.2'
|
3
|
-
require 'activerecord'
|
4
|
-
|
5
|
-
module Soup
|
6
|
-
module Tuples
|
7
|
-
class ActiveRecordTuple < ActiveRecord::Base
|
8
|
-
set_table_name :tuples
|
9
|
-
|
10
|
-
def self.prepare_database(config)
|
11
|
-
ActiveRecord::Base.establish_connection(config)
|
12
|
-
return if connection.tables.include?("tuples") # NOTE - this probably isn't good enough (what if the schema has changed?)
|
13
|
-
ActiveRecord::Migration.create_table :tuples, :force => true do |t|
|
14
|
-
t.column :snip_id, :integer
|
15
|
-
t.column :name, :string
|
16
|
-
t.column :value, :text
|
17
|
-
t.column :created_at, :datetime
|
18
|
-
t.column :updated_at, :datetime
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
def self.for_snip(id)
|
23
|
-
find_all_by_snip_id(id)
|
24
|
-
end
|
25
|
-
|
26
|
-
def self.find_matching(name, value_conditions=nil)
|
27
|
-
condition_sql = "name = '#{name}'"
|
28
|
-
condition_sql += " AND value #{value_conditions}" if value_conditions
|
29
|
-
find(:all, :conditions => condition_sql)
|
30
|
-
end
|
31
|
-
|
32
|
-
# TODO: *totally* not threadsafe.
|
33
|
-
def self.next_snip_id
|
34
|
-
maximum(:snip_id) + 1 rescue 1
|
35
|
-
end
|
36
|
-
|
37
|
-
def save
|
38
|
-
super if new_record? || dirty?
|
39
|
-
end
|
40
|
-
|
41
|
-
private
|
42
|
-
|
43
|
-
def dirty?
|
44
|
-
true # hmm.
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
@@ -1,50 +0,0 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
gem 'datamapper'
|
3
|
-
require 'data_mapper'
|
4
|
-
|
5
|
-
module Soup
|
6
|
-
module Tuples
|
7
|
-
|
8
|
-
# This tuple implementation is broken - there's a weird interaction
|
9
|
-
# where values are not set within the web application.
|
10
|
-
#
|
11
|
-
class DataMapperTuple < DataMapper::Base
|
12
|
-
def self.prepare_database(config)
|
13
|
-
DataMapper::Database.setup(config)
|
14
|
-
# NOTE: so um, this property stuff doesn't like it if you're not connected to the db
|
15
|
-
# lets only have it once we are? Seems mental.
|
16
|
-
self.class_eval {
|
17
|
-
set_table_name 'tuples'
|
18
|
-
|
19
|
-
property :snip_id, :integer
|
20
|
-
|
21
|
-
property :name, :string
|
22
|
-
property :value, :text
|
23
|
-
|
24
|
-
property :created_at, :datetime
|
25
|
-
property :updated_at, :datetime
|
26
|
-
}
|
27
|
-
return if self.table.exists? # NOTE - this probably isn't good enough (what if the schema has changed?)
|
28
|
-
DataMapper::Persistence.auto_migrate!
|
29
|
-
end
|
30
|
-
|
31
|
-
def self.for_snip(id)
|
32
|
-
all(:snip_id => id)
|
33
|
-
end
|
34
|
-
|
35
|
-
# TODO: *totally* not threadsafe.
|
36
|
-
def self.next_snip_id
|
37
|
-
database.query("SELECT MAX(snip_id) + 1 FROM tuples")[0] || 1
|
38
|
-
end
|
39
|
-
|
40
|
-
def save
|
41
|
-
if dirty? or new_record?
|
42
|
-
super
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
alias_method :destroy, :destroy!
|
47
|
-
end
|
48
|
-
|
49
|
-
end
|
50
|
-
end
|
@@ -1,36 +0,0 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
gem 'sequel'
|
3
|
-
require 'sequel'
|
4
|
-
|
5
|
-
DB = Sequel.sqlite 'soup_development.db'
|
6
|
-
|
7
|
-
module Soup
|
8
|
-
module Tuples
|
9
|
-
|
10
|
-
class SequelTuple < Sequel::Model(:tuples)
|
11
|
-
set_schema do
|
12
|
-
primary_key :id
|
13
|
-
integer :snip_id
|
14
|
-
string :name
|
15
|
-
string :value
|
16
|
-
datetime :created_at
|
17
|
-
datetime :updated_at
|
18
|
-
end
|
19
|
-
|
20
|
-
def self.prepare_database(config)
|
21
|
-
# ummm... how to connect?
|
22
|
-
create_table # TODO: detect if the table already exists
|
23
|
-
end
|
24
|
-
|
25
|
-
def self.for_snip(id)
|
26
|
-
filter(:snip_id => id).to_a
|
27
|
-
end
|
28
|
-
|
29
|
-
# TODO: *totally* not threadsafe.
|
30
|
-
def self.next_snip_id
|
31
|
-
max(:snip_id) + 1
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
end
|
36
|
-
end
|
data/soup.gemspec
DELETED
@@ -1,68 +0,0 @@
|
|
1
|
-
|
2
|
-
# Gem::Specification for Soup-0.2.1
|
3
|
-
# Originally generated by Echoe
|
4
|
-
|
5
|
-
Gem::Specification.new do |s|
|
6
|
-
s.name = %q{soup}
|
7
|
-
s.version = "0.2.1"
|
8
|
-
s.date = %q{2008-04-17}
|
9
|
-
s.summary = %q{Soup is a bit of everything, summoned from nothing. Soup is like an imaginary friend - comforting,}
|
10
|
-
s.email = ["james@lazyatom.com"]
|
11
|
-
s.homepage = %q{}
|
12
|
-
s.rubyforge_project = %q{soup}
|
13
|
-
s.description = %q{Soup is a bit of everything, summoned from nothing. Soup is like an imaginary friend - comforting,}
|
14
|
-
s.has_rdoc = true
|
15
|
-
s.authors = ["James Adam"]
|
16
|
-
s.files = ["lib/soup/empty_class.rb", "lib/soup/snip.rb", "lib/soup/tuples/active_record_tuple.rb", "lib/soup/tuples/data_mapper_tuple.rb", "lib/soup/tuples/sequel_tuple.rb", "lib/soup.rb", "Manifest", "README", "spec/snip_spec.rb", "spec/soup_spec.rb", "spec/spec_helper.rb", "spec/spec_runner.rb", "soup.gemspec"]
|
17
|
-
s.add_dependency(%q<activerecord>, [">= 2.0.2"])
|
18
|
-
end
|
19
|
-
|
20
|
-
|
21
|
-
# # Original Rakefile source (requires the Echoe gem):
|
22
|
-
#
|
23
|
-
# require 'rubygems'
|
24
|
-
#
|
25
|
-
# require 'lib/soup'
|
26
|
-
#
|
27
|
-
# begin
|
28
|
-
# require 'echoe'
|
29
|
-
#
|
30
|
-
# Echoe.new("soup", Soup::VERSION) do |soup|
|
31
|
-
# soup.author = ["James Adam"]
|
32
|
-
# soup.email = ["james@lazyatom.com"]
|
33
|
-
# soup.description = File.readlines("README").first
|
34
|
-
# soup.dependencies = ["activerecord >=2.0.2"]
|
35
|
-
# soup.clean_pattern = ["*.db", "meta", "pkg"]
|
36
|
-
# soup.ignore_pattern = [".git", "*.db", "meta"]
|
37
|
-
# end
|
38
|
-
#
|
39
|
-
# rescue LoadError
|
40
|
-
# puts "You need to install the echoe gem to perform meta operations on this gem"
|
41
|
-
# end
|
42
|
-
#
|
43
|
-
# begin
|
44
|
-
# require 'spec'
|
45
|
-
# require 'spec/rake/spectask'
|
46
|
-
#
|
47
|
-
# Spec::Rake::SpecTask.new do |t|
|
48
|
-
# t.spec_opts = ["--format", "specdoc", "--colour"]
|
49
|
-
# t.spec_files = Dir['spec/**/*_spec.rb'].sort
|
50
|
-
# t.libs = ['lib']
|
51
|
-
# #t.rcov = true
|
52
|
-
# #t.rcov_dir = 'meta/coverage'
|
53
|
-
# end
|
54
|
-
#
|
55
|
-
# task :show_rcov do
|
56
|
-
# system 'open meta/coverage/index.html' if PLATFORM['darwin']
|
57
|
-
# end
|
58
|
-
#
|
59
|
-
# rescue LoadError
|
60
|
-
# puts "You need RSpec installed to run the spec (default) task on this gem"
|
61
|
-
# end
|
62
|
-
#
|
63
|
-
# desc "Open an irb session preloaded with this library"
|
64
|
-
# task :console do
|
65
|
-
# sh "irb --prompt simple -rubygems -r ./lib/soup.rb"
|
66
|
-
# end
|
67
|
-
#
|
68
|
-
# task :default => [:spec, :show_rcov]
|
data/spec/snip_spec.rb
DELETED
@@ -1,102 +0,0 @@
|
|
1
|
-
require File.join(File.dirname(__FILE__), "spec_helper")
|
2
|
-
|
3
|
-
describe Snip do
|
4
|
-
before(:each) do
|
5
|
-
Soup.base = {:database => "soup_test.db"}
|
6
|
-
Soup.prepare
|
7
|
-
clear_database
|
8
|
-
end
|
9
|
-
|
10
|
-
describe "when newly created" do
|
11
|
-
before(:each) { @snip = Snip.new }
|
12
|
-
|
13
|
-
it "should not have a name" do
|
14
|
-
@snip.name.should be_nil
|
15
|
-
end
|
16
|
-
|
17
|
-
it "should return nil for any attributes" do
|
18
|
-
@snip.other_attribute.should be_nil
|
19
|
-
end
|
20
|
-
|
21
|
-
it "should not have an id yet" do
|
22
|
-
@snip.id.should be_nil
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
describe "when being created with attributes" do
|
27
|
-
|
28
|
-
it "should set attributes as passed in" do
|
29
|
-
@snip = Snip.new(:beats => 'phat', :rhymes => 100)
|
30
|
-
@snip.beats.should == 'phat'
|
31
|
-
@snip.rhymes.should == 100
|
32
|
-
end
|
33
|
-
|
34
|
-
it "should ignore any id passed in" do
|
35
|
-
@snip = Snip.new(:id => 1000)
|
36
|
-
@snip.id.should be_nil
|
37
|
-
end
|
38
|
-
|
39
|
-
it "should not ignore the secret __id" do
|
40
|
-
@snip = Snip.new(:__id => 1000)
|
41
|
-
@snip.id.should == 1000
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
describe "when setting attributes" do
|
46
|
-
before(:each) { @snip = Snip.new }
|
47
|
-
|
48
|
-
it "should allow setting attributes" do
|
49
|
-
@snip.something = "blah"
|
50
|
-
@snip.something.should == "blah"
|
51
|
-
end
|
52
|
-
|
53
|
-
it "should not respond to attributes that have not been set" do
|
54
|
-
@snip.should_not respond_to(:monkey)
|
55
|
-
end
|
56
|
-
|
57
|
-
it "should respond to attributes that have been set" do
|
58
|
-
@snip.monkey = true
|
59
|
-
@snip.should respond_to(:monkey)
|
60
|
-
end
|
61
|
-
|
62
|
-
it "should not allow setting of the id" do
|
63
|
-
@snip.id = 100
|
64
|
-
@snip.id.should_not == 100
|
65
|
-
@snip.id.should be_nil
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
describe "when saving" do
|
70
|
-
before(:each) { @snip = Snip.new }
|
71
|
-
|
72
|
-
it "should not save if there's no data" do
|
73
|
-
lambda { @snip.save }.should raise_error
|
74
|
-
end
|
75
|
-
|
76
|
-
it "should return all attributes when reloading" do
|
77
|
-
@snip.name = "something"
|
78
|
-
@snip.jazz = "smooth"
|
79
|
-
@snip.save
|
80
|
-
|
81
|
-
other_snip = Soup['something']
|
82
|
-
other_snip.jazz.should == "smooth"
|
83
|
-
end
|
84
|
-
|
85
|
-
it "should generate an id" do
|
86
|
-
@snip.name = "something"
|
87
|
-
@snip.save
|
88
|
-
|
89
|
-
other_snip = Soup['something']
|
90
|
-
other_snip.id.should_not be_nil
|
91
|
-
end
|
92
|
-
|
93
|
-
it "should not overwrite an existing id created via __id" do
|
94
|
-
@snip = Snip.new(:__id => 100)
|
95
|
-
@snip.name = "something_else"
|
96
|
-
@snip.save
|
97
|
-
|
98
|
-
other_snip = Soup['something_else']
|
99
|
-
other_snip.id.should == 100
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
data/spec/soup_spec.rb
DELETED
@@ -1,148 +0,0 @@
|
|
1
|
-
require 'soup'
|
2
|
-
|
3
|
-
describe Soup do
|
4
|
-
|
5
|
-
describe "when unflavoured or based" do
|
6
|
-
before(:each) { Soup.class_eval { @database_config = nil; @tuple_class = nil } }
|
7
|
-
it "should use the default database config" do
|
8
|
-
# I think this set of mock / expectations might be super wrong
|
9
|
-
Soup::DEFAULT_CONFIG.should_receive(:merge).with({}).and_return(Soup::DEFAULT_CONFIG)
|
10
|
-
Soup.tuple_class.should_receive(:prepare_database).with(Soup::DEFAULT_CONFIG)
|
11
|
-
Soup.prepare
|
12
|
-
end
|
13
|
-
|
14
|
-
it "should use the default tuple implementation" do
|
15
|
-
# No real idea how to mock the require, or use aught but Secret Knowledge that AR == Default
|
16
|
-
Soup.tuple_class.should == Soup::Tuples::ActiveRecordTuple
|
17
|
-
Soup::Tuples::ActiveRecordTuple.should_receive(:prepare_database)
|
18
|
-
Soup.prepare
|
19
|
-
end
|
20
|
-
|
21
|
-
end
|
22
|
-
|
23
|
-
describe "when being based" do
|
24
|
-
before(:each) { Soup.class_eval { @database_config = nil; @tuple_class = nil } }
|
25
|
-
|
26
|
-
it "should allow the base of the soup to be set" do
|
27
|
-
Soup.should respond_to(:base=)
|
28
|
-
end
|
29
|
-
|
30
|
-
it "should use the new base when preparing the soup" do
|
31
|
-
bouillabaisse = {:database => 'fishy.db', :adapter => 'fishdb'}
|
32
|
-
Soup.base = bouillabaisse
|
33
|
-
Soup.tuple_class.should_receive(:prepare_database).with(bouillabaisse)
|
34
|
-
Soup.prepare
|
35
|
-
end
|
36
|
-
|
37
|
-
it "should merge incomplete bases with the default" do
|
38
|
-
tasteless = {:database => 'water.db'}
|
39
|
-
Soup.base = tasteless
|
40
|
-
Soup.tuple_class.should_receive(:prepare_database).with(Soup::DEFAULT_CONFIG.merge(tasteless))
|
41
|
-
Soup.prepare
|
42
|
-
end
|
43
|
-
|
44
|
-
it "should allow the base to be reset" do
|
45
|
-
bouillabaisse = {:database => 'fishy.db', :adapter => 'fishdb'}
|
46
|
-
Soup.base = bouillabaisse
|
47
|
-
Soup.tuple_class.should_receive(:prepare_database).once.with(bouillabaisse).ordered
|
48
|
-
Soup.prepare
|
49
|
-
|
50
|
-
gazpacho = {:database => 'tomato.db', :adapter => 'colddb'}
|
51
|
-
Soup.base = gazpacho
|
52
|
-
Soup.tuple_class.should_receive(:prepare_database).once.with(gazpacho).ordered
|
53
|
-
Soup.prepare
|
54
|
-
end
|
55
|
-
|
56
|
-
it "should not allow old bases to interfere with new ones" do
|
57
|
-
bouillabaisse = {:database => 'fishy.db', :adapter => 'fishdb'}
|
58
|
-
Soup.base = bouillabaisse
|
59
|
-
Soup.tuple_class.should_receive(:prepare_database).once.with(bouillabaisse).ordered
|
60
|
-
Soup.prepare
|
61
|
-
|
62
|
-
tasteless = {:database => 'water.db'}
|
63
|
-
Soup.base = tasteless
|
64
|
-
Soup.tuple_class.should_receive(:prepare_database).once.with(Soup::DEFAULT_CONFIG.merge(tasteless)).ordered
|
65
|
-
Soup.tuple_class.should_not_receive(:prepare_database).with(bouillabaisse.merge(tasteless))
|
66
|
-
Soup.prepare
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
describe "when being flavoured" do
|
71
|
-
before(:each) { Soup.class_eval { @database_config = nil; @tuple_class = nil } }
|
72
|
-
|
73
|
-
it "should allow the soup to be flavoured" do
|
74
|
-
Soup.should respond_to(:flavour=)
|
75
|
-
end
|
76
|
-
|
77
|
-
it "should determine the tuple class based on the flavour" do
|
78
|
-
require 'soup/tuples/data_mapper_tuple'
|
79
|
-
Soup.flavour = :data_mapper
|
80
|
-
Soup.tuple_class.should == Soup::Tuples::DataMapperTuple
|
81
|
-
end
|
82
|
-
|
83
|
-
it "should allow the flavour to be set multiple times" do
|
84
|
-
require 'soup/tuples/data_mapper_tuple'
|
85
|
-
Soup.flavour = :data_mapper
|
86
|
-
Soup.tuple_class.should == Soup::Tuples::DataMapperTuple
|
87
|
-
|
88
|
-
require 'soup/tuples/sequel_tuple'
|
89
|
-
Soup.flavour = :sequel
|
90
|
-
Soup.tuple_class.should_not == Soup::Tuples::DataMapperTuple
|
91
|
-
Soup.tuple_class.should == Soup::Tuples::SequelTuple
|
92
|
-
end
|
93
|
-
|
94
|
-
it "should use have no tuple class if the flavour is unknowable" do
|
95
|
-
Soup.flavour = :shoggoth
|
96
|
-
Soup.tuple_class.should == nil
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
describe "when adding data to the Soup directly" do
|
101
|
-
before(:each) do
|
102
|
-
Soup.base = {:database => "soup_test.db"}
|
103
|
-
Soup.flavour = :active_record
|
104
|
-
Soup.prepare
|
105
|
-
clear_database
|
106
|
-
end
|
107
|
-
|
108
|
-
it "should create a new snip" do
|
109
|
-
attributes = {:name => 'monkey'}
|
110
|
-
Snip.should_receive(:new).with(attributes).and_return(mock('snip', :null_object => true))
|
111
|
-
Soup << attributes
|
112
|
-
end
|
113
|
-
|
114
|
-
it "should save the snip" do
|
115
|
-
attributes = {:name => 'monkey'}
|
116
|
-
Snip.should_receive(:new).with(attributes).and_return(snip = mock('snip'))
|
117
|
-
snip.should_receive(:save)
|
118
|
-
Soup << attributes
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
describe "when sieving the soup" do
|
123
|
-
before(:each) do
|
124
|
-
Soup.base = {:database => "soup_test.db"}
|
125
|
-
Soup.flavour = :active_record
|
126
|
-
Soup.prepare
|
127
|
-
clear_database
|
128
|
-
@james = Soup << {:name => 'james', :spirit_guide => 'fox', :colour => 'blue', :powers => 'yes'}
|
129
|
-
@murray = Soup << {:name => 'murray', :spirit_guide => 'chaffinch', :colour => 'red', :powers => 'yes'}
|
130
|
-
end
|
131
|
-
|
132
|
-
it "should find snips by name if the parameter is a string" do
|
133
|
-
Soup['james'].should == @james
|
134
|
-
end
|
135
|
-
|
136
|
-
it "should find snips using exact matching of keys and values if the parameter is a hash" do
|
137
|
-
Soup[:name => 'murray'].should == @murray
|
138
|
-
end
|
139
|
-
|
140
|
-
it "should match using all parameters" do
|
141
|
-
Soup[:powers => 'yes', :colour => 'red'].should == @james
|
142
|
-
end
|
143
|
-
|
144
|
-
it "should return an array if more than one snip matches" do
|
145
|
-
Soup[:powers => 'yes'].should == [@james, @murray]
|
146
|
-
end
|
147
|
-
end
|
148
|
-
end
|
data/spec/spec_helper.rb
DELETED
data/spec/spec_runner.rb
DELETED
@@ -1,12 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
# We need to do a bit of extra work here, because Snip will undefine a
|
4
|
-
# whole bunch of stuff that spec adds, like the Object#should method.
|
5
|
-
$LOAD_PATH.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
|
6
|
-
require 'soup'
|
7
|
-
|
8
|
-
# This is basically the contents of rspec's own spec runner. It's just
|
9
|
-
# that we needed to require 'soup' before it.
|
10
|
-
require 'rubygems'
|
11
|
-
require 'spec'
|
12
|
-
exit ::Spec::Runner::CommandLine.run(rspec_options)
|