soup 0.2.1 → 0.9.9
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/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)
|