cubbyhole 0.1.1 → 0.2.0
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/.gitignore +2 -0
- data/Gemfile +1 -0
- data/README.md +1 -2
- data/example/Gemfile +1 -0
- data/lib/cubbyhole/base.rb +113 -0
- data/lib/cubbyhole/collection.rb +43 -0
- data/lib/cubbyhole/sdbm.rb +23 -0
- data/lib/cubbyhole/version.rb +1 -1
- data/lib/cubbyhole.rb +3 -75
- data/spec/collections_spec.rb +42 -0
- data/spec/cubbyhole_spec.rb +8 -0
- data/spec/hash_backend_spec.rb +73 -0
- metadata +12 -5
data/.gitignore
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Cubbyhole - The zero config, non-production,
|
1
|
+
# Cubbyhole - The zero config, non-production, datastore
|
2
2
|
|
3
3
|
Sinatra is dead simple, but it's more interesting to play with if you can store some data. The problem is that setting up ActiveRecord or DataMapper significantly complicates things.
|
4
4
|
|
@@ -7,7 +7,6 @@ Cubbyhole is designed to be absolutely zero config.
|
|
7
7
|
```ruby
|
8
8
|
require 'cubbyhole'
|
9
9
|
|
10
|
-
|
11
10
|
post = Post.create(:title => "Foo", :text => "Bar")
|
12
11
|
|
13
12
|
Post.get(post.id).title # => Foo
|
data/example/Gemfile
CHANGED
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'cubbyhole/collection'
|
2
|
+
|
3
|
+
module Cubbyhole
|
4
|
+
class Base
|
5
|
+
METHOD_BLACKLIST = [:marshal_load, :marshal_dump, :_dump, :_load]
|
6
|
+
|
7
|
+
def self.create(params={})
|
8
|
+
new(params).save
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.get(id)
|
12
|
+
backend[id.to_s]
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.get!(id)
|
16
|
+
get(id) or raise "#{self.class} not found for id #{id}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.next_id
|
20
|
+
@next_id ||= 0
|
21
|
+
id = @next_id
|
22
|
+
@next_id += 1
|
23
|
+
id
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.all
|
27
|
+
sdbm.values.map{|x| Marshal.load(x) }
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.first(atts = nil)
|
31
|
+
all(atts).first
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.last(atts = nil)
|
35
|
+
all(atts).last
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.all(atts = nil)
|
39
|
+
Collection.new(backend.keys.sort_by(&:to_i).map{|k| backend[k] }).all(atts)
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.backend
|
43
|
+
require 'cubbyhole/sdbm'
|
44
|
+
@backend ||= SDBM.new(self.to_s)
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.nuke
|
48
|
+
backend.clear
|
49
|
+
end
|
50
|
+
|
51
|
+
def initialize(params={})
|
52
|
+
@id = self.class.next_id
|
53
|
+
@params = params
|
54
|
+
@persisted = false
|
55
|
+
normalize_params!
|
56
|
+
end
|
57
|
+
|
58
|
+
attr_reader :id
|
59
|
+
|
60
|
+
def persisted?
|
61
|
+
@persisted
|
62
|
+
end
|
63
|
+
|
64
|
+
def save
|
65
|
+
@persisted = true
|
66
|
+
normalize_params!
|
67
|
+
self.class.backend[id.to_s] = self
|
68
|
+
self
|
69
|
+
end
|
70
|
+
|
71
|
+
def destroy
|
72
|
+
self.class.backend.delete(@id.to_s)
|
73
|
+
end
|
74
|
+
|
75
|
+
def method_missing(meth, *args, &blk)
|
76
|
+
return super if METHOD_BLACKLIST.include?(meth.to_sym)
|
77
|
+
|
78
|
+
key = meth.to_s
|
79
|
+
|
80
|
+
if key =~ /=$/
|
81
|
+
raise ArgumentError unless args.size == 1
|
82
|
+
@params[key.gsub(/=$/, "")] = args.first
|
83
|
+
else
|
84
|
+
return super if args.size != 0
|
85
|
+
@params[key]
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def respond_to?(meth)
|
90
|
+
return super if METHOD_BLACKLIST.include?(meth.to_sym)
|
91
|
+
true
|
92
|
+
end
|
93
|
+
|
94
|
+
def attributes
|
95
|
+
@params.dup
|
96
|
+
end
|
97
|
+
|
98
|
+
def update_attributes(params)
|
99
|
+
@params.merge!(params)
|
100
|
+
normalize_params!
|
101
|
+
save
|
102
|
+
end
|
103
|
+
|
104
|
+
def normalize_params!
|
105
|
+
@params.default = nil
|
106
|
+
|
107
|
+
@params.keys.dup.each do |key|
|
108
|
+
@params[key.to_s] = @params.delete(key)
|
109
|
+
end
|
110
|
+
@params
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Cubbyhole
|
2
|
+
class Collection < Array
|
3
|
+
|
4
|
+
def last(atts = nil)
|
5
|
+
if atts
|
6
|
+
all(atts).last
|
7
|
+
else
|
8
|
+
super()
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def first(atts = nil)
|
13
|
+
if atts
|
14
|
+
all(atts).first
|
15
|
+
else
|
16
|
+
super()
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def all(atts = nil)
|
21
|
+
if atts
|
22
|
+
Collection.new(where(atts))
|
23
|
+
else
|
24
|
+
self
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def where(atts)
|
31
|
+
select do |item|
|
32
|
+
matches = true
|
33
|
+
atts.each do |k, v|
|
34
|
+
if item.send(k).to_s != v.to_s
|
35
|
+
matches = false
|
36
|
+
end
|
37
|
+
end
|
38
|
+
matches
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'sdbm'
|
2
|
+
|
3
|
+
module Cubbyhole
|
4
|
+
class SDBM
|
5
|
+
extend Forwardable
|
6
|
+
def_delegators :@sdbm, :keys, :delete, :clear
|
7
|
+
|
8
|
+
def initialize(name)
|
9
|
+
@sdbm = ::SDBM.new("cubbyhole.#{name}.sdbm")
|
10
|
+
end
|
11
|
+
|
12
|
+
def [](key)
|
13
|
+
if str = @sdbm[key]
|
14
|
+
Marshal.load(str)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def []=(key, val)
|
19
|
+
@sdbm[key] = Marshal.dump(val)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
data/lib/cubbyhole/version.rb
CHANGED
data/lib/cubbyhole.rb
CHANGED
@@ -1,78 +1,6 @@
|
|
1
|
-
require
|
1
|
+
require 'cubbyhole/version'
|
2
|
+
require 'cubbyhole/base'
|
2
3
|
|
3
4
|
def Object.const_missing(name)
|
4
|
-
|
5
|
-
end
|
6
|
-
|
7
|
-
module Cubbyhole
|
8
|
-
class Base
|
9
|
-
def self.create(params={})
|
10
|
-
new(params).save
|
11
|
-
end
|
12
|
-
|
13
|
-
def self.get(id)
|
14
|
-
objs[id]
|
15
|
-
end
|
16
|
-
|
17
|
-
def self.next_id
|
18
|
-
@next_id ||= 0
|
19
|
-
id = @next_id
|
20
|
-
@next_id += 1
|
21
|
-
id
|
22
|
-
end
|
23
|
-
|
24
|
-
def self.all
|
25
|
-
objs.values
|
26
|
-
end
|
27
|
-
|
28
|
-
def self.objs
|
29
|
-
@objs ||= {}
|
30
|
-
end
|
31
|
-
|
32
|
-
def initialize(params={})
|
33
|
-
@id = self.class.next_id
|
34
|
-
@params = params
|
35
|
-
@persisted = false
|
36
|
-
stringify_keys!
|
37
|
-
end
|
38
|
-
|
39
|
-
attr_reader :id
|
40
|
-
|
41
|
-
def persisted?
|
42
|
-
@persisted
|
43
|
-
end
|
44
|
-
|
45
|
-
def save
|
46
|
-
@persisted = true
|
47
|
-
stringify_keys!
|
48
|
-
self.class.objs[@id] = self
|
49
|
-
end
|
50
|
-
|
51
|
-
def method_missing(meth, *args, &blk)
|
52
|
-
key = meth.to_s
|
53
|
-
|
54
|
-
if key =~ /=$/
|
55
|
-
raise ArgumentError unless args.size == 1
|
56
|
-
@params[key.gsub(/=$/, "")] = args.first
|
57
|
-
else
|
58
|
-
raise ArgumentError unless args.size == 0
|
59
|
-
@params[key]
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
def respond_to?(*args); true; end
|
64
|
-
|
65
|
-
def update_attributes(params)
|
66
|
-
@params.merge!(params)
|
67
|
-
stringify_keys!
|
68
|
-
save
|
69
|
-
end
|
70
|
-
|
71
|
-
def stringify_keys!
|
72
|
-
@params.keys.each do |key|
|
73
|
-
@params[key.to_s] = @params.delete(key)
|
74
|
-
end
|
75
|
-
@params
|
76
|
-
end
|
77
|
-
end
|
5
|
+
const_set(name, Class.new(Cubbyhole::Base))
|
78
6
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
$:.push File.expand_path("../../lib", __FILE__)
|
2
|
+
|
3
|
+
require 'cubbyhole'
|
4
|
+
|
5
|
+
describe Cubbyhole::Collection do
|
6
|
+
|
7
|
+
describe "with a bunch of things" do
|
8
|
+
before do
|
9
|
+
Thing.nuke
|
10
|
+
Thing.create(:name => "thing1", :color => "brown", :shape => "triangle")
|
11
|
+
Thing.create(:name => "thing2", :color => "green", :shape => "triangle")
|
12
|
+
Thing.create(:name => "thing3", :color => "brown", :shape => "triangle")
|
13
|
+
Thing.create(:name => "thing4", :color => "green", :shape => "square")
|
14
|
+
Thing.create(:name => "thing5", :color => "blue", :shape => "triangle")
|
15
|
+
end
|
16
|
+
|
17
|
+
it "can find them all" do
|
18
|
+
Thing.all.size.should eq 5
|
19
|
+
Thing.all(:color => "green").size.should eq 2
|
20
|
+
end
|
21
|
+
|
22
|
+
it "can find the first" do
|
23
|
+
Thing.first.name.should eq "thing1"
|
24
|
+
Thing.first(:color => "green").name.should eq "thing2"
|
25
|
+
end
|
26
|
+
|
27
|
+
it "can find the last" do
|
28
|
+
Thing.last.name.should eq "thing5"
|
29
|
+
Thing.last(:color => "brown").name.should eq "thing3"
|
30
|
+
end
|
31
|
+
|
32
|
+
it "can chain finds" do
|
33
|
+
browntriangles = Thing.all(:color => "brown").all(:shape => "triangle")
|
34
|
+
browntriangles.size.should eq 2
|
35
|
+
browntriangles.last.name.should eq "thing3"
|
36
|
+
|
37
|
+
bluetriangle = Thing.all(:shape => "triangle").first(:color => "blue")
|
38
|
+
bluetriangle.name.should eq "thing5"
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
data/spec/cubbyhole_spec.rb
CHANGED
@@ -0,0 +1,73 @@
|
|
1
|
+
$:.push File.expand_path("../../lib", __FILE__)
|
2
|
+
|
3
|
+
require 'cubbyhole/base'
|
4
|
+
|
5
|
+
class BaseModel < Cubbyhole::Base
|
6
|
+
def self.backend
|
7
|
+
@backend ||= Hash.new
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class MyModelModel < BaseModel; end
|
12
|
+
class MyOtherModel < BaseModel; end
|
13
|
+
|
14
|
+
describe "Cubbyhole::Base subclass with self.backend of Hash.new" do
|
15
|
+
|
16
|
+
it "allows you to set attrs on new" do
|
17
|
+
model = MyModelModel.new(:foo => "bar", :baz => "bang")
|
18
|
+
model.foo.should == "bar"
|
19
|
+
model.baz.should == "bang"
|
20
|
+
end
|
21
|
+
|
22
|
+
it "allows you to save and fetch objects" do
|
23
|
+
model = MyModelModel.new(:foo => "bar")
|
24
|
+
model.save
|
25
|
+
|
26
|
+
fetched = MyModelModel.get(model.id)
|
27
|
+
fetched.foo.should == "bar"
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should not persist unsaved objects" do
|
31
|
+
model = MyModelModel.new(:foo => "bar")
|
32
|
+
|
33
|
+
MyModelModel.get(model.id).should be_nil
|
34
|
+
end
|
35
|
+
|
36
|
+
it "allows you to create saved objects" do
|
37
|
+
model = MyModelModel.create(:foo => "bar")
|
38
|
+
|
39
|
+
fetched = MyModelModel.get(model.id)
|
40
|
+
fetched.foo.should == "bar"
|
41
|
+
end
|
42
|
+
|
43
|
+
it "allows you to set and retrieve attributes" do
|
44
|
+
model = MyModelModel.new
|
45
|
+
model.foo.should == nil
|
46
|
+
model.foo = "bar"
|
47
|
+
model.foo.should == "bar"
|
48
|
+
end
|
49
|
+
|
50
|
+
it "allows you to update_attributes" do
|
51
|
+
model = MyModelModel.new(:foo => "bar")
|
52
|
+
model.save
|
53
|
+
model.update_attributes(:foo => "baz", :bar => "zot")
|
54
|
+
|
55
|
+
fetched = MyModelModel.get(model.id)
|
56
|
+
fetched.foo.should == "baz"
|
57
|
+
fetched.bar.should == "zot"
|
58
|
+
end
|
59
|
+
|
60
|
+
it "finds all objects" do
|
61
|
+
3.times { MyOtherModel.create }
|
62
|
+
|
63
|
+
MyOtherModel.all.size.should == 3
|
64
|
+
end
|
65
|
+
|
66
|
+
it "allows you to destroy objects" do
|
67
|
+
object = MyOtherModel.create
|
68
|
+
id = object.id
|
69
|
+
|
70
|
+
object.destroy
|
71
|
+
MyOtherModel.get(object.id).should be_nil
|
72
|
+
end
|
73
|
+
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cubbyhole
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 23
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 0.
|
8
|
+
- 2
|
9
|
+
- 0
|
10
|
+
version: 0.2.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Andy Delcambre
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-
|
18
|
+
date: 2011-10-05 00:00:00 -07:00
|
19
19
|
default_executable:
|
20
20
|
dependencies: []
|
21
21
|
|
@@ -41,8 +41,13 @@ files:
|
|
41
41
|
- example/views/index.haml
|
42
42
|
- example/views/new.haml
|
43
43
|
- lib/cubbyhole.rb
|
44
|
+
- lib/cubbyhole/base.rb
|
45
|
+
- lib/cubbyhole/collection.rb
|
46
|
+
- lib/cubbyhole/sdbm.rb
|
44
47
|
- lib/cubbyhole/version.rb
|
48
|
+
- spec/collections_spec.rb
|
45
49
|
- spec/cubbyhole_spec.rb
|
50
|
+
- spec/hash_backend_spec.rb
|
46
51
|
has_rdoc: true
|
47
52
|
homepage: ""
|
48
53
|
licenses: []
|
@@ -78,4 +83,6 @@ signing_key:
|
|
78
83
|
specification_version: 3
|
79
84
|
summary: A zero config data store
|
80
85
|
test_files:
|
86
|
+
- spec/collections_spec.rb
|
81
87
|
- spec/cubbyhole_spec.rb
|
88
|
+
- spec/hash_backend_spec.rb
|