ampere 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.rspec +1 -0
- data/.rvmrc +1 -0
- data/Gemfile +18 -0
- data/Gemfile.lock +51 -0
- data/LICENSE.txt +70 -0
- data/README.md +89 -0
- data/Rakefile +49 -0
- data/VERSION +1 -0
- data/features/ampere.feature +9 -0
- data/features/step_definitions/ampere_steps.rb +0 -0
- data/features/support/env.rb +15 -0
- data/lib/ampere.rb +26 -0
- data/lib/ampere/collection.rb +71 -0
- data/lib/ampere/model.rb +315 -0
- data/spec/models/indices_spec.rb +141 -0
- data/spec/models/model_spec.rb +208 -0
- data/spec/models/queries_spec.rb +97 -0
- data/spec/models/relationships/belongs_to_spec.rb +100 -0
- data/spec/models/relationships/has_many_spec.rb +71 -0
- data/spec/models/relationships/has_one_spec.rb +75 -0
- data/spec/models/updates_spec.rb +62 -0
- data/spec/module/ampere_spec.rb +26 -0
- data/spec/module/collections_spec.rb +71 -0
- data/spec/spec_helper.rb +20 -0
- data/test/helper.rb +18 -0
- data/test/test_ampere.rb +7 -0
- metadata +166 -0
@@ -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
|