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 CHANGED
@@ -3,3 +3,5 @@
3
3
  Gemfile.lock
4
4
  pkg/*
5
5
  vendor/bundle
6
+ *.dir
7
+ *.pag
data/Gemfile CHANGED
@@ -5,4 +5,5 @@ gemspec
5
5
 
6
6
  group :test do
7
7
  gem 'rspec'
8
+ gem 'ruby-debug'
8
9
  end
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Cubbyhole - The zero config, non-production, activemodel compliant datastore
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
@@ -3,3 +3,4 @@ source :rubygems
3
3
  gem "cubbyhole", :path => ".."
4
4
  gem "sinatra"
5
5
  gem "haml"
6
+ gem "ruby-debug"
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Cubbyhole
2
- VERSION = "0.1.1"
2
+ VERSION = "0.2.0"
3
3
  end
data/lib/cubbyhole.rb CHANGED
@@ -1,78 +1,6 @@
1
- require "cubbyhole/version"
1
+ require 'cubbyhole/version'
2
+ require 'cubbyhole/base'
2
3
 
3
4
  def Object.const_missing(name)
4
- eval "#{name} = Class.new(Cubbyhole::Base)"
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
@@ -56,4 +56,12 @@ describe Cubbyhole do
56
56
 
57
57
  MyModel.all.size.should == 3
58
58
  end
59
+
60
+ it "allows you to destroy objects" do
61
+ object = MyModel.create
62
+ id = object.id
63
+
64
+ object.destroy
65
+ MyModel.get(object.id).should be_nil
66
+ end
59
67
  end
@@ -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: 25
4
+ hash: 23
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 1
9
- - 1
10
- version: 0.1.1
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-08-19 00:00:00 -07:00
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