ampere 0.1.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.
@@ -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