ampere 0.1.0 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/ampere.gemspec +88 -0
- data/lib/ampere/model.rb +37 -10
- data/spec/models/model_spec.rb +31 -22
- data/spec/models/queries_spec.rb +1 -1
- data/spec/models/relationships/belongs_to_spec.rb +9 -1
- data/spec/models/relationships/has_one_spec.rb +1 -1
- data/spec/module/collections_spec.rb +1 -0
- metadata +20 -19
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.2
|
data/ampere.gemspec
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "ampere"
|
8
|
+
s.version = "0.1.2"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Max Thom Stahl"]
|
12
|
+
s.date = "2012-01-18"
|
13
|
+
s.description = "An ActiveRecord/Mongoid-esque object model for the Redis key/value data store."
|
14
|
+
s.email = "max@villainousindustri.es"
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE.txt",
|
17
|
+
"README.md"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
".rspec",
|
22
|
+
".rvmrc",
|
23
|
+
"Gemfile",
|
24
|
+
"Gemfile.lock",
|
25
|
+
"LICENSE.txt",
|
26
|
+
"README.md",
|
27
|
+
"Rakefile",
|
28
|
+
"VERSION",
|
29
|
+
"ampere.gemspec",
|
30
|
+
"features/ampere.feature",
|
31
|
+
"features/step_definitions/ampere_steps.rb",
|
32
|
+
"features/support/env.rb",
|
33
|
+
"lib/ampere.rb",
|
34
|
+
"lib/ampere/collection.rb",
|
35
|
+
"lib/ampere/model.rb",
|
36
|
+
"spec/models/indices_spec.rb",
|
37
|
+
"spec/models/model_spec.rb",
|
38
|
+
"spec/models/queries_spec.rb",
|
39
|
+
"spec/models/relationships/belongs_to_spec.rb",
|
40
|
+
"spec/models/relationships/has_many_spec.rb",
|
41
|
+
"spec/models/relationships/has_one_spec.rb",
|
42
|
+
"spec/models/updates_spec.rb",
|
43
|
+
"spec/module/ampere_spec.rb",
|
44
|
+
"spec/module/collections_spec.rb",
|
45
|
+
"spec/spec_helper.rb",
|
46
|
+
"test/helper.rb",
|
47
|
+
"test/test_ampere.rb"
|
48
|
+
]
|
49
|
+
s.homepage = "http://github.com/mstahl/ampere"
|
50
|
+
s.licenses = ["EPL"]
|
51
|
+
s.require_paths = ["lib"]
|
52
|
+
s.rubygems_version = "1.8.10"
|
53
|
+
s.summary = "A pure Ruby ORM for Redis."
|
54
|
+
|
55
|
+
if s.respond_to? :specification_version then
|
56
|
+
s.specification_version = 3
|
57
|
+
|
58
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
59
|
+
s.add_runtime_dependency(%q<redis>, [">= 0"])
|
60
|
+
s.add_development_dependency(%q<shoulda>, [">= 0"])
|
61
|
+
s.add_development_dependency(%q<cucumber>, [">= 0"])
|
62
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
63
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.6.4"])
|
64
|
+
s.add_development_dependency(%q<simplecov>, [">= 0"])
|
65
|
+
s.add_development_dependency(%q<rspec>, [">= 0"])
|
66
|
+
s.add_development_dependency(%q<rdoc>, [">= 0"])
|
67
|
+
else
|
68
|
+
s.add_dependency(%q<redis>, [">= 0"])
|
69
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
70
|
+
s.add_dependency(%q<cucumber>, [">= 0"])
|
71
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
72
|
+
s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
|
73
|
+
s.add_dependency(%q<simplecov>, [">= 0"])
|
74
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
75
|
+
s.add_dependency(%q<rdoc>, [">= 0"])
|
76
|
+
end
|
77
|
+
else
|
78
|
+
s.add_dependency(%q<redis>, [">= 0"])
|
79
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
80
|
+
s.add_dependency(%q<cucumber>, [">= 0"])
|
81
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
82
|
+
s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
|
83
|
+
s.add_dependency(%q<simplecov>, [">= 0"])
|
84
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
85
|
+
s.add_dependency(%q<rdoc>, [">= 0"])
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
data/lib/ampere/model.rb
CHANGED
@@ -5,6 +5,7 @@ module Ampere
|
|
5
5
|
@fields = []
|
6
6
|
@field_defaults = {}
|
7
7
|
@indices = []
|
8
|
+
@field_types = {}
|
8
9
|
|
9
10
|
### Instance methods
|
10
11
|
|
@@ -45,12 +46,12 @@ module Ampere
|
|
45
46
|
# Initialize an instance like this:
|
46
47
|
#
|
47
48
|
# Post.new :title => "Kitties: Are They Awesome?"
|
48
|
-
def initialize(hash = {})
|
49
|
+
def initialize(hash = {}, unmarshal = false)
|
49
50
|
hash.each do |k, v|
|
50
51
|
if k == 'id' then
|
51
|
-
@id = v
|
52
|
+
@id = unmarshal ? Marshal.load(v) : v
|
52
53
|
else
|
53
|
-
self.send("#{k}=", v)
|
54
|
+
self.send("#{k}=", (unmarshal and not k =~ /_id$/) ? Marshal.load(v) : v)
|
54
55
|
end
|
55
56
|
end
|
56
57
|
end
|
@@ -67,7 +68,12 @@ module Ampere
|
|
67
68
|
end
|
68
69
|
|
69
70
|
self.class.fields.each do |k|
|
70
|
-
|
71
|
+
v = Ampere.connection.hget(@id, k)
|
72
|
+
if k =~ /_id$/ then
|
73
|
+
self.send("#{k}=", v)
|
74
|
+
else
|
75
|
+
self.send("#{k}=", Marshal.load(v))
|
76
|
+
end
|
71
77
|
end
|
72
78
|
self
|
73
79
|
end
|
@@ -80,7 +86,7 @@ module Ampere
|
|
80
86
|
end
|
81
87
|
|
82
88
|
self.attributes.each do |k, v|
|
83
|
-
Ampere.connection.hset(@id, k, v)
|
89
|
+
Ampere.connection.hset(@id, k, k =~ /_id$/ ? v : Marshal.dump(v))
|
84
90
|
end
|
85
91
|
|
86
92
|
self.class.indices.each do |index|
|
@@ -108,7 +114,7 @@ module Ampere
|
|
108
114
|
def update_attribute(key, value)
|
109
115
|
raise "Cannot update a nonexistent field!" unless self.class.fields.include?(key)
|
110
116
|
self.send("#{key}=", value)
|
111
|
-
Ampere.connection.hset(@id, key, value)
|
117
|
+
Ampere.connection.hset(@id, key, Marshal.dump(value))
|
112
118
|
end
|
113
119
|
|
114
120
|
def update_attributes(hash = {})
|
@@ -158,17 +164,31 @@ module Ampere
|
|
158
164
|
@fields ||= []
|
159
165
|
@field_defaults ||= {}
|
160
166
|
@indices ||= []
|
167
|
+
@field_types ||= {}
|
161
168
|
|
162
169
|
@fields << name
|
163
170
|
|
164
|
-
attr_accessor :"#{name}"
|
171
|
+
# attr_accessor :"#{name}"
|
165
172
|
|
166
173
|
# Handle default value
|
167
174
|
@field_defaults[name] = options[:default]
|
168
175
|
|
176
|
+
# Handle type, if any
|
177
|
+
if options[:type] then
|
178
|
+
@field_types[:"#{name}"] = options[:type].to_s
|
179
|
+
end
|
180
|
+
|
169
181
|
define_method :"#{name}" do
|
170
182
|
instance_variable_get("@#{name}") or self.class.field_defaults[name]
|
171
183
|
end
|
184
|
+
|
185
|
+
define_method :"#{name}=" do |val|
|
186
|
+
if not self.class.field_types[:"#{name}"] or val.is_a?(eval(self.class.field_types[:"#{name}"])) then
|
187
|
+
instance_variable_set("@#{name}", val)
|
188
|
+
else
|
189
|
+
raise "Cannot set field of type #{self.class.field_types[name.to_sym]} with #{val.class} value"
|
190
|
+
end
|
191
|
+
end
|
172
192
|
end
|
173
193
|
|
174
194
|
def self.fields
|
@@ -179,11 +199,15 @@ module Ampere
|
|
179
199
|
@field_defaults
|
180
200
|
end
|
181
201
|
|
202
|
+
def self.field_types
|
203
|
+
@field_types
|
204
|
+
end
|
205
|
+
|
182
206
|
# Finds the record with the given ID, or the first that matches the given conditions
|
183
207
|
def self.find(options = {})
|
184
208
|
if options.class == String then
|
185
209
|
if Ampere.connection.exists(options) then
|
186
|
-
new(Ampere.connection.hgetall(options))
|
210
|
+
new(Ampere.connection.hgetall(options), true)
|
187
211
|
else
|
188
212
|
nil
|
189
213
|
end
|
@@ -239,6 +263,7 @@ module Ampere
|
|
239
263
|
@fields ||= []
|
240
264
|
@field_defaults ||= {}
|
241
265
|
@indices ||= []
|
266
|
+
@field_types ||= {}
|
242
267
|
if field_name.class == String or field_name.class == Symbol then
|
243
268
|
raise "Can't index a nonexistent field!" unless @fields.include?(field_name)
|
244
269
|
elsif field_name.class == Array then
|
@@ -290,9 +315,11 @@ module Ampere
|
|
290
315
|
|
291
316
|
unless nonindexed_fields.empty?
|
292
317
|
results = all if results.nil?
|
293
|
-
results.map
|
318
|
+
results = results.to_a.map{|r| r.class == String ? find(r) : r}
|
294
319
|
nonindexed_fields.each do |key|
|
295
|
-
results.select!{|r|
|
320
|
+
results.select!{|r|
|
321
|
+
r.send(key) == options[key]
|
322
|
+
}
|
296
323
|
end
|
297
324
|
end
|
298
325
|
|
data/spec/models/model_spec.rb
CHANGED
@@ -59,11 +59,16 @@ describe "Base models", :model => true do
|
|
59
59
|
end
|
60
60
|
end
|
61
61
|
|
62
|
+
it "should set field types for fields that have a type" do
|
63
|
+
Post.field_types.should_not be_nil
|
64
|
+
Post.field_types[:pageviews].should_not be_nil
|
65
|
+
Post.field_types[:pageviews].should == 'Integer'
|
66
|
+
end
|
67
|
+
|
62
68
|
it "shouldn't care what the value types are assigned to a field with no type defined" do
|
63
|
-
|
64
|
-
# Assign a string to the :make of a motorcycle.
|
69
|
+
# Assign an int to the :title of a Post.
|
65
70
|
(->{
|
66
|
-
Post.create :title =>
|
71
|
+
Post.create :title => 1234,
|
67
72
|
:byline => "",
|
68
73
|
:content => "",
|
69
74
|
:pageviews => 1234
|
@@ -71,25 +76,24 @@ describe "Base models", :model => true do
|
|
71
76
|
|
72
77
|
}).should_not raise_error
|
73
78
|
|
74
|
-
# Assign
|
79
|
+
# Assign a string to the :title of a Post.
|
75
80
|
(->{
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
81
|
+
Post.create :title => "1234",
|
82
|
+
:byline => "",
|
83
|
+
:content => "",
|
84
|
+
:pageviews => 1234
|
80
85
|
}).should_not raise_error
|
81
86
|
end
|
82
87
|
|
83
|
-
it "should, given a field's type, only accept values for that field of that type"
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
:displacement => 450.0
|
88
|
+
it "should, given a field's type, only accept values for that field of that type" do
|
89
|
+
post = Post.create :title => "",
|
90
|
+
:byline => "",
|
91
|
+
:content => "",
|
92
|
+
:pageviews => 1234
|
89
93
|
|
90
94
|
# Try to assign a string to :year, raising an error.
|
91
|
-
(->{
|
92
|
-
(->{
|
95
|
+
(->{post.pageviews = "this is not an integer"}).should raise_error
|
96
|
+
(->{post.pageviews = 4321}).should_not raise_error
|
93
97
|
end
|
94
98
|
end
|
95
99
|
end
|
@@ -141,9 +145,15 @@ describe "Base models", :model => true do
|
|
141
145
|
foo = Post.create :title => "Kitties!", :byline => "Max", :content => "Kitties are awesome."
|
142
146
|
bar = Post.create :title => "Doggies!", :byline => "Max", :content => "Doggies are cool."
|
143
147
|
|
144
|
-
foo.should == foo
|
148
|
+
foo.should == foo
|
145
149
|
foo.should_not == bar
|
146
150
|
|
151
|
+
foo.should eql(foo)
|
152
|
+
foo.should_not eql(bar)
|
153
|
+
end
|
154
|
+
|
155
|
+
it "should also be able to determine the hash of a record, for equivalence testing" do
|
156
|
+
Post.new(title: "Guinea Pigs", byline: "Max", content: "Guinea pigs are super cute.").hash.is_a?(Fixnum).should be_true
|
147
157
|
end
|
148
158
|
|
149
159
|
it "should be able to tell when it's new" do
|
@@ -167,7 +177,7 @@ describe "Base models", :model => true do
|
|
167
177
|
|
168
178
|
it "should be destroyable by itself" do
|
169
179
|
another_post = Post.create :title => "This one too, probably.",
|
170
|
-
:byline => "Just seems like one
|
180
|
+
:byline => "Just seems like one big",
|
171
181
|
:content => "non sequitor."
|
172
182
|
id = another_post.id
|
173
183
|
another_post.destroy.should == 1
|
@@ -175,10 +185,9 @@ describe "Base models", :model => true do
|
|
175
185
|
end
|
176
186
|
|
177
187
|
it "should be findable by ID" do
|
178
|
-
post = Post.
|
179
|
-
|
180
|
-
|
181
|
-
post.save
|
188
|
+
post = Post.create :title => "foo",
|
189
|
+
:byline => "bar",
|
190
|
+
:content => "baz"
|
182
191
|
Post.find(post.id).should == post
|
183
192
|
# Since we're using GUIDs, this should also be true:
|
184
193
|
post2 = Post.find(post.id)
|
data/spec/models/queries_spec.rb
CHANGED
@@ -77,7 +77,7 @@ describe 'queries', :queries => true do
|
|
77
77
|
|
78
78
|
it 'should be able to find by two non-indexed fields at once' do
|
79
79
|
# :breed and :age are not indexed
|
80
|
-
Kitty.where(:breed => "Domestic shorthair", :age =>
|
80
|
+
Kitty.where(:breed => "Domestic shorthair", :age => 17).count.should == 1
|
81
81
|
end
|
82
82
|
|
83
83
|
it 'should be able to find by a mix of indexed and non-indexed fields' do
|
@@ -88,7 +88,15 @@ describe 'belongs_to relationships', :belongs_to => true do
|
|
88
88
|
end
|
89
89
|
|
90
90
|
it 'sets belongs_to pointer for has_many relationship' do
|
91
|
-
|
91
|
+
@car.passengers = @car.passengers + [@driver]
|
92
|
+
@car.passengers = @car.passengers + [@passenger]
|
93
|
+
@car.save
|
94
|
+
|
95
|
+
@driver.reload
|
96
|
+
@passenger.reload
|
97
|
+
|
98
|
+
@driver.car.should == @car
|
99
|
+
@passenger.car.should == @car
|
92
100
|
end
|
93
101
|
|
94
102
|
###
|
@@ -27,6 +27,7 @@ describe 'Collections', :collections => true do
|
|
27
27
|
democrats.class.should == Ampere::Collection
|
28
28
|
democrats.model.should == President
|
29
29
|
democrats.raw_array.length.should == 3
|
30
|
+
democrats.length.should == 3
|
30
31
|
end
|
31
32
|
|
32
33
|
it 'should be accessible via [] like an Array' do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ampere
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-01-
|
12
|
+
date: 2012-01-18 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: redis
|
16
|
-
requirement: &
|
16
|
+
requirement: &70220576682780 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70220576682780
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: shoulda
|
27
|
-
requirement: &
|
27
|
+
requirement: &70220576678180 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70220576678180
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: cucumber
|
38
|
-
requirement: &
|
38
|
+
requirement: &70220576676920 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70220576676920
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: bundler
|
49
|
-
requirement: &
|
49
|
+
requirement: &70220576675420 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ~>
|
@@ -54,10 +54,10 @@ dependencies:
|
|
54
54
|
version: 1.0.0
|
55
55
|
type: :development
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *70220576675420
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: jeweler
|
60
|
-
requirement: &
|
60
|
+
requirement: &70220576674400 !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|
63
63
|
- - ~>
|
@@ -65,10 +65,10 @@ dependencies:
|
|
65
65
|
version: 1.6.4
|
66
66
|
type: :development
|
67
67
|
prerelease: false
|
68
|
-
version_requirements: *
|
68
|
+
version_requirements: *70220576674400
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: simplecov
|
71
|
-
requirement: &
|
71
|
+
requirement: &70220576672900 !ruby/object:Gem::Requirement
|
72
72
|
none: false
|
73
73
|
requirements:
|
74
74
|
- - ! '>='
|
@@ -76,10 +76,10 @@ dependencies:
|
|
76
76
|
version: '0'
|
77
77
|
type: :development
|
78
78
|
prerelease: false
|
79
|
-
version_requirements: *
|
79
|
+
version_requirements: *70220576672900
|
80
80
|
- !ruby/object:Gem::Dependency
|
81
81
|
name: rspec
|
82
|
-
requirement: &
|
82
|
+
requirement: &70220576671880 !ruby/object:Gem::Requirement
|
83
83
|
none: false
|
84
84
|
requirements:
|
85
85
|
- - ! '>='
|
@@ -87,10 +87,10 @@ dependencies:
|
|
87
87
|
version: '0'
|
88
88
|
type: :development
|
89
89
|
prerelease: false
|
90
|
-
version_requirements: *
|
90
|
+
version_requirements: *70220576671880
|
91
91
|
- !ruby/object:Gem::Dependency
|
92
92
|
name: rdoc
|
93
|
-
requirement: &
|
93
|
+
requirement: &70220576670820 !ruby/object:Gem::Requirement
|
94
94
|
none: false
|
95
95
|
requirements:
|
96
96
|
- - ! '>='
|
@@ -98,7 +98,7 @@ dependencies:
|
|
98
98
|
version: '0'
|
99
99
|
type: :development
|
100
100
|
prerelease: false
|
101
|
-
version_requirements: *
|
101
|
+
version_requirements: *70220576670820
|
102
102
|
description: An ActiveRecord/Mongoid-esque object model for the Redis key/value data
|
103
103
|
store.
|
104
104
|
email: max@villainousindustri.es
|
@@ -117,6 +117,7 @@ files:
|
|
117
117
|
- README.md
|
118
118
|
- Rakefile
|
119
119
|
- VERSION
|
120
|
+
- ampere.gemspec
|
120
121
|
- features/ampere.feature
|
121
122
|
- features/step_definitions/ampere_steps.rb
|
122
123
|
- features/support/env.rb
|
@@ -150,7 +151,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
150
151
|
version: '0'
|
151
152
|
segments:
|
152
153
|
- 0
|
153
|
-
hash:
|
154
|
+
hash: 4395967643533566831
|
154
155
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
155
156
|
none: false
|
156
157
|
requirements:
|