ampere 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,141 @@
1
+ require File.join(File.dirname(__FILE__), "..", "spec_helper.rb")
2
+
3
+ describe "Model indices", :indices => true do
4
+ before :all do
5
+ Redis.new.flushall
6
+ Ampere.connect
7
+
8
+ class Student < Ampere::Model
9
+ field :first_name
10
+ field :last_name
11
+ field :student_id_num
12
+
13
+ index :last_name
14
+ index :student_id_num, :unique => true
15
+ end
16
+
17
+ @a = Student.create :first_name => "Hannibal",
18
+ :last_name => "Smith",
19
+ :student_id_num => "1001"
20
+ @b = Student.create :first_name => "Cindy",
21
+ :last_name => "Smith",
22
+ :student_id_num => "1002"
23
+ @c = Student.create :first_name => "Emmanuel",
24
+ :last_name => "Goldstein",
25
+ :student_id_num => "1003"
26
+ end
27
+
28
+ ###
29
+
30
+ it 'should know about its own indices' do
31
+ Student.indices.should include(:last_name)
32
+ Student.indices.should_not include(:first_name)
33
+ Student.indices.should include(:student_id_num)
34
+ end
35
+
36
+ it 'should find an array of values for a non-unique index' do
37
+ smiths = Student.where(:last_name => "Smith")
38
+ smiths.should_not be_empty
39
+ smiths.to_a.map(&:first_name).should include("Hannibal")
40
+ smiths.to_a.map(&:first_name).should include("Cindy")
41
+ end
42
+
43
+ it 'should find a single value for a unique index' do
44
+ emmanuel = Student.where(:student_id_num => "1003")
45
+ emmanuel.should_not be_empty
46
+ emmanuel.first.first_name.should == "Emmanuel"
47
+ end
48
+
49
+ it 'should refuse to create an index on a field that does not exist' do
50
+ (->{
51
+ class Student < Ampere::Model
52
+ field :this_field_exists
53
+
54
+ index :this_field_exists
55
+ end
56
+ }).should_not raise_error
57
+ (->{
58
+ class Student < Ampere::Model
59
+ index :this_field_does_not_exist
60
+ end
61
+ }).should raise_error
62
+ (->{
63
+ class Student < Ampere::Model
64
+ field :this_field_exists
65
+
66
+ index [:this_field_exists, :but_this_one_does_not]
67
+ end
68
+ }).should raise_error
69
+ end
70
+
71
+ it 'should enforce the uniqueness of unique single-field indices' do
72
+ pending
73
+ # The student_id_num field of Student is unique. If two Students
74
+ # with the same student_id_num are stored, the second should not
75
+ # save successfully, throwing an exception instead.
76
+ (->{
77
+ Student.create :first_name => "Bobby",
78
+ :last_name => "Tables",
79
+ :student_id_num => "2000"
80
+ }).should_not raise_error
81
+ (->{
82
+ Student.create :first_name => "Johnny",
83
+ :last_name => "Tables",
84
+ :student_id_num => "2000"
85
+ }).should raise_error
86
+ Student.where(:student_id_num => "2000").first.first_name.should == "Bobby"
87
+ end
88
+
89
+ context 'compound indices' do
90
+ before :all do
91
+ class Professor < Ampere::Model
92
+ field :first_name
93
+ field :last_name
94
+ field :employee_id_number
95
+
96
+ index :employee_id_number
97
+ index [:last_name, :first_name]
98
+ end
99
+ end
100
+
101
+ ###
102
+
103
+ it 'should define compound indices' do
104
+ Professor.indices.should include([:first_name, :last_name])
105
+
106
+ Ampere.connection.exists("ampere.index.professor.first_name:last_name").should be_false
107
+ Professor.create :first_name => "Warren",
108
+ :last_name => "Satterfield",
109
+ :employee_id_number => "31415926"
110
+ Professor.count.should == 1
111
+ Ampere.connection.exists("ampere.index.professor.first_name:last_name").should be_true
112
+ end
113
+
114
+ it 'should be able to search on both fields of a compound index' do
115
+ Professor.create :first_name => "Ava",
116
+ :last_name => "Jenkins",
117
+ :employee_id_number => "31415927"
118
+ Professor.create :first_name => "Lazaro",
119
+ :last_name => "Adams",
120
+ :employee_id_number => "31415928"
121
+ Professor.create :first_name => "Brandi",
122
+ :last_name => "O'Kon",
123
+ :employee_id_number => "31415929"
124
+
125
+ brandi = Professor.where(:first_name => "Brandi", :last_name => "O'Kon")
126
+ brandi.count.should == 1
127
+ brandi.first.employee_id_number.should == "31415929"
128
+ end
129
+
130
+ it 'should still be able to search on just one field of a compound index' do
131
+ pending
132
+ end
133
+ end
134
+
135
+ ###
136
+
137
+ after :all do
138
+ Redis.new.flushall
139
+ Ampere.disconnect
140
+ end
141
+ end
@@ -0,0 +1,208 @@
1
+ require File.join(File.dirname(__FILE__), "..", "spec_helper.rb")
2
+
3
+ describe "Base models", :model => true do
4
+ context "by themselves" do
5
+ before :all do
6
+ Ampere.connect
7
+
8
+ # Clear out Redis
9
+ Redis.new.flushall
10
+
11
+ # Define a model class here.
12
+ class Post < Ampere::Model
13
+ field :title
14
+ field :byline
15
+ field :content
16
+ end
17
+
18
+ end
19
+
20
+ # # #
21
+
22
+ context 'fields' do
23
+ it "should define attr accessors and suchlike" do
24
+ post = Post.new
25
+ post.should respond_to(:title)
26
+ post.should respond_to(:"title=")
27
+ post.should respond_to(:byline)
28
+ post.should respond_to(:"byline=")
29
+ post.should respond_to(:content)
30
+ post.should respond_to(:"content=")
31
+ end
32
+
33
+ it "should know about its own fields" do
34
+ Post.fields.should include(:title)
35
+ Post.fields.should include(:byline)
36
+ Post.fields.should include(:content)
37
+ end
38
+
39
+ it "should have default values definable" do
40
+ class Comment < Ampere::Model
41
+ field :subject, :default => "No subject"
42
+ field :content
43
+ end
44
+
45
+ comment = Comment.new
46
+ comment.subject.should == "No subject"
47
+ comment.content.should be_nil
48
+ end
49
+
50
+ it "should raise an exception when you try to set a field that doesn't exist" do
51
+ (->{Post.new :shazbot => "This isn't the right key"}).should raise_error
52
+ end
53
+
54
+ context 'types', :types => true do
55
+ before :all do
56
+ # Just adding a field with a type to Post
57
+ class Post < Ampere::Model
58
+ field :pageviews, :type => Integer, :default => 0
59
+ end
60
+ end
61
+
62
+ it "shouldn't care what the value types are assigned to a field with no type defined" do
63
+ pending "Types not implemented yet."
64
+ # Assign a string to the :make of a motorcycle.
65
+ (->{
66
+ Post.create :title => "",
67
+ :byline => "",
68
+ :content => "",
69
+ :pageviews => 1234
70
+
71
+
72
+ }).should_not raise_error
73
+
74
+ # Assign an integer to the :make of a motorcycle, for some reason.
75
+ (->{
76
+ Motorcycle.create :make => 1234,
77
+ :model => 'CB450SC',
78
+ :year => 1986,
79
+ :displacement => 450.0
80
+ }).should_not raise_error
81
+ end
82
+
83
+ it "should, given a field's type, only accept values for that field of that type", :wip => true do
84
+ pending "Types not implemented yet."
85
+ cycle = Motorcycle.create :make => 'Honda',
86
+ :model => 'CB450SC',
87
+ :year => 1986,
88
+ :displacement => 450.0
89
+
90
+ # Try to assign a string to :year, raising an error.
91
+ (->{cycle.year = "this is not a year"}).should raise_error
92
+ (->{cycle.year = 1996}).should_not raise_error
93
+ end
94
+ end
95
+ end
96
+
97
+ it "should have a 'new' method that works like we'd expect" do
98
+ post = Post.new :title => "Amish Give Up - 'This is bullshit!', Elders Say",
99
+ :byline => "The Onion",
100
+ :content => %{
101
+ Lorem ipsum dolor sit amet, consectetur adipisicing
102
+ elit, sed do eiusmod tempor incididunt ut labore et
103
+ dolore magna aliqua.
104
+ }
105
+ post.title.should == "Amish Give Up - 'This is bullshit!', Elders Say"
106
+ post.byline.should == "The Onion"
107
+ post.content.should =~ /Lorem ipsum dolor/
108
+ end
109
+
110
+ it "should be able to convert itself to a hash" do
111
+ post = Post.new :title => "A title",
112
+ :byline => "Max",
113
+ :content => "Some content"
114
+ hash = post.attributes
115
+ hash[:title].should == "A title"
116
+ hash[:byline].should == "Max"
117
+ hash[:content].should == "Some content"
118
+ end
119
+
120
+ it "should have a 'save' and 'reload' method that work like we'd expect" do
121
+ post = Post.new :title => "A title",
122
+ :byline => "Max",
123
+ :content => "Some content"
124
+
125
+ post.save.should be_true
126
+
127
+ post.reload
128
+ post.title.should == "A title"
129
+ post.byline.should == "Max"
130
+ post.content.should == "Some content"
131
+ end
132
+
133
+ it "should refuse to reload a new record, that hasn't yet been saved" do
134
+ post = Post.new :title => "A title",
135
+ :byline => "Max",
136
+ :content => "Some content"
137
+ (->{post.reload}).should raise_error
138
+ end
139
+
140
+ it "should be able to tell when two records are equivalent" do
141
+ foo = Post.create :title => "Kitties!", :byline => "Max", :content => "Kitties are awesome."
142
+ bar = Post.create :title => "Doggies!", :byline => "Max", :content => "Doggies are cool."
143
+
144
+ foo.should == foo # Post.new(:title => "Kitties!", :byline => "Max", :content => "Kitties are awesome.")
145
+ foo.should_not == bar
146
+
147
+ end
148
+
149
+ it "should be able to tell when it's new" do
150
+ post = Post.new :title => "A title",
151
+ :byline => "Max",
152
+ :content => "Some content"
153
+ post.new?.should be_true
154
+ post.save
155
+ post.new?.should be_false
156
+ end
157
+
158
+ it "should be deleteable from the model class" do
159
+ post = Post.create :title => "This post should be deleted",
160
+ :byline => "because it's awful",
161
+ :content => "and it doesn't even make sense."
162
+ id = post.id
163
+ post.should_not be_nil
164
+ Post.delete(id).should == 1
165
+ Post.find(id).should be_nil
166
+ end
167
+
168
+ it "should be destroyable by itself" do
169
+ another_post = Post.create :title => "This one too, probably.",
170
+ :byline => "Just seems like one bit",
171
+ :content => "non sequitor."
172
+ id = another_post.id
173
+ another_post.destroy.should == 1
174
+ Post.find(id).should be_nil
175
+ end
176
+
177
+ it "should be findable by ID" do
178
+ post = Post.new :title => "foo",
179
+ :byline => "bar",
180
+ :content => "baz"
181
+ post.save
182
+ Post.find(post.id).should == post
183
+ # Since we're using GUIDs, this should also be true:
184
+ post2 = Post.find(post.id)
185
+
186
+ post.title.should == post2.title
187
+ post.byline.should == post2.byline
188
+ post.content.should == post2.content
189
+ end
190
+
191
+ it "should be able to save itself upon creation" do
192
+ post = Post.create :title => "Another title",
193
+ :byline => "Max",
194
+ :content => "Some other content"
195
+ Post.find(post.id).should == post
196
+ end
197
+
198
+ # # #
199
+
200
+ after :all do
201
+ # Clear out Redis
202
+ Redis.new.flushall
203
+ Ampere.disconnect
204
+ end
205
+
206
+ end
207
+
208
+ end
@@ -0,0 +1,97 @@
1
+ require File.join(File.dirname(__FILE__), "..", "spec_helper.rb")
2
+
3
+ describe 'queries', :queries => true do
4
+ before :each do
5
+ Redis.new.flushall
6
+ Ampere.connect
7
+
8
+ class Kitty < Ampere::Model
9
+ field :name
10
+ field :breed
11
+ field :age
12
+ field :color
13
+
14
+ index :name # => Unique
15
+ index :color # => Non-unique
16
+ end
17
+
18
+ @kitty_paws = Kitty.create name: 'Kitty Paws',
19
+ breed: 'Domestic shorthair',
20
+ age: 19,
21
+ color: 'orange'
22
+ @nate = Kitty.create name: 'Nate',
23
+ breed: 'Domestic shorthair',
24
+ age: 17,
25
+ color: 'black'
26
+ @jinxii = Kitty.create name: 'Jinxii',
27
+ breed: 'Chartreux',
28
+ age: 3,
29
+ color: 'grey'
30
+ @italics = Kitty.create name: 'Italics',
31
+ breed: 'Siberian',
32
+ age: 7,
33
+ color: 'orange'
34
+ @serif = Kitty.create name: 'Serif',
35
+ breed: 'Siberian',
36
+ age: 5,
37
+ color: 'grey'
38
+ [@kitty_paws, @nate, @jinxii, @italics, @serif]. each {|k| k.reload}
39
+ end
40
+
41
+ ###
42
+
43
+ it 'should pass a basic sanity check' do
44
+ Kitty.count.should == 5
45
+ end
46
+
47
+ it 'should be able to select all kitties' do
48
+ Kitty.all.map(&:name).should include('Kitty Paws')
49
+ Kitty.all.map(&:name).should include('Nate')
50
+ Kitty.all.map(&:name).should include('Jinxii')
51
+ Kitty.all.map(&:name).should include('Italics')
52
+ Kitty.all.map(&:name).should include('Serif')
53
+ end
54
+
55
+ context 'with no fields' do
56
+ it 'should return the empty set with no conditions given' do
57
+ Kitty.where().should be_empty
58
+ end
59
+ end
60
+
61
+ context 'with one field' do
62
+ it 'should be able to find by an indexed field using where()' do
63
+ Kitty.where(:name => 'Nate').to_a.map(&:name).should include('Nate')
64
+ end
65
+
66
+ it 'should be able to find by a non-indexed field using where()' do
67
+ Kitty.where(:breed => 'Siberian').to_a.map(&:name).should include('Italics')
68
+ Kitty.where(:breed => 'Siberian').to_a.map(&:name).should include('Serif')
69
+ end
70
+ end
71
+
72
+ it 'should be able to find by two indexed fields at once' do
73
+ kitty = Kitty.where(:name => "Kitty Paws", :color => "orange")
74
+ kitty.count.should == 1
75
+ kitty.first.name.should == "Kitty Paws"
76
+ end
77
+
78
+ it 'should be able to find by two non-indexed fields at once' do
79
+ # :breed and :age are not indexed
80
+ Kitty.where(:breed => "Domestic shorthair", :age => "17").count.should == 1
81
+ end
82
+
83
+ it 'should be able to find by a mix of indexed and non-indexed fields' do
84
+ # :color is indexed, :breed is not
85
+ Kitty.where(:color => "orange").count.should == 2
86
+ Kitty.where(:breed => "Siberian").count.should == 2
87
+ Kitty.where(:color => "orange", :breed => "Siberian").count.should == 1
88
+ Kitty.where(:color => "orange", :breed => "Siberian").first.name.should == "Italics"
89
+ end
90
+
91
+ ###
92
+
93
+ after :all do
94
+ Redis.new.flushall
95
+ Ampere.connect
96
+ end
97
+ end
@@ -0,0 +1,100 @@
1
+ require File.join(File.dirname(__FILE__), "..", "..", "spec_helper.rb")
2
+
3
+ describe 'belongs_to relationships', :belongs_to => true do
4
+ before :all do
5
+ Redis.new.flushall
6
+ Ampere.connect
7
+
8
+ # These are used by the has_one/belongs_to example below
9
+ class Car < Ampere::Model
10
+ field :make
11
+ field :model
12
+ field :year
13
+
14
+ has_one :engine
15
+ has_many :passengers
16
+ end
17
+
18
+ class Engine < Ampere::Model
19
+ field :displacement
20
+ field :cylinders
21
+ field :configuration
22
+
23
+ belongs_to :car
24
+ end
25
+
26
+ class Passenger < Ampere::Model
27
+ field :name
28
+ field :seat
29
+
30
+ belongs_to :car
31
+ end
32
+
33
+ @car = Car.create :make => "Lamborghini",
34
+ :model => "Countach",
35
+ :year => "1974"
36
+ @engine = Engine.create :displacement => "5167",
37
+ :cylinders => "12",
38
+ :configuration => "V"
39
+ @driver = Passenger.create :name => "Max",
40
+ :seat => "driver"
41
+ @passenger = Passenger.create :name => "Leila",
42
+ :seat => "passenger"
43
+ end
44
+
45
+ ###
46
+
47
+ it 'sets accessor methods for a belongs_to relationship' do
48
+ # Other side of a has_many
49
+ @driver.should respond_to(:car)
50
+ # Other side of a has_one
51
+ @engine.should respond_to(:car)
52
+ end
53
+
54
+ it 'sets belongs_to pointer for has_one relationship that is set from the parent' do
55
+ @car.engine = @engine
56
+ @car.save
57
+ @engine.save
58
+
59
+ car = Car.find(@car.id)
60
+ engine = Engine.find(@engine.id)
61
+
62
+ car.engine.should == @engine
63
+ engine.car.should == @car
64
+
65
+ # Cleanup
66
+ @car.engine = nil
67
+ @engine.car = nil
68
+ @car.save
69
+ @engine.save
70
+ end
71
+
72
+ it 'sets belongs_to pointer for has_one relationship that is set from the child' do
73
+ @engine.car = @car
74
+ @car.save
75
+ @engine.save
76
+
77
+ car = Car.find(@car.id)
78
+ engine = Engine.find(@engine.id)
79
+
80
+ car.engine.should == @engine
81
+ engine.car.should == @car
82
+
83
+ # Cleanup
84
+ @car.engine = nil
85
+ @engine.car = nil
86
+ @car.save
87
+ @engine.save
88
+ end
89
+
90
+ it 'sets belongs_to pointer for has_many relationship' do
91
+ pending
92
+ end
93
+
94
+ ###
95
+
96
+ after :all do
97
+ Redis.new.flushall
98
+ Ampere.connect
99
+ end
100
+ end