cassandra_complex 0.5
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/.document +5 -0
- data/CHANGES.txt +9 -0
- data/Gemfile +20 -0
- data/Gemfile.lock +57 -0
- data/LICENSE.txt +201 -0
- data/README.md +64 -0
- data/Rakefile +52 -0
- data/VERSION +1 -0
- data/cassandra_complex.gemspec +77 -0
- data/lib/cassandra_complex.rb +7 -0
- data/lib/cassandra_complex/configuration.rb +59 -0
- data/lib/cassandra_complex/connection.rb +168 -0
- data/lib/cassandra_complex/index.rb +39 -0
- data/lib/cassandra_complex/model.rb +309 -0
- data/lib/cassandra_complex/row.rb +10 -0
- data/lib/cassandra_complex/table.rb +271 -0
- data/spec/cassandra_complex/model_spec.rb +203 -0
- data/spec/cassandra_complex/table_spec.rb +372 -0
- data/spec/spec_helper.rb +2 -0
- data/test/benchmark.rb +38 -0
- metadata +182 -0
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
module CassandraComplex
|
|
2
|
+
# Class Table which wraps CQL3 operations
|
|
3
|
+
#
|
|
4
|
+
# @!attribute [rw] keyspace
|
|
5
|
+
# @return [String] The keyspace is being connected to
|
|
6
|
+
# @!attribute [rw] configuration
|
|
7
|
+
# @return [String] The configuration of the connection
|
|
8
|
+
# @example Selecting all rows with given primary
|
|
9
|
+
# class Timeline < CassandraComplex::Table
|
|
10
|
+
# set_table_name 'timeline'
|
|
11
|
+
# end
|
|
12
|
+
#
|
|
13
|
+
# rows = Timeline.all('some_primary_key') do |row|
|
|
14
|
+
# # this puts is outputed on each row is being fetch
|
|
15
|
+
# puts row['body']
|
|
16
|
+
# end
|
|
17
|
+
# rows.each do |row|
|
|
18
|
+
# # puts is being outputed when all rows are fetched from Cassandra
|
|
19
|
+
# puts row['body']
|
|
20
|
+
# end
|
|
21
|
+
class Table
|
|
22
|
+
#not neccessary to allow .new
|
|
23
|
+
private_class_method :new
|
|
24
|
+
|
|
25
|
+
class << self
|
|
26
|
+
|
|
27
|
+
attr_accessor :keyspace
|
|
28
|
+
attr_accessor :configuration
|
|
29
|
+
|
|
30
|
+
# Specify keyspace for particular table
|
|
31
|
+
#
|
|
32
|
+
# @param [String] kyspc keyspace
|
|
33
|
+
def set_keyspace(kyspc)
|
|
34
|
+
self.keyspace = kyspc
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Return connection for given keyspace(or default keyspace if no keyspace is given)
|
|
38
|
+
#
|
|
39
|
+
# @param [String] kyspc keyspace for which return the connection
|
|
40
|
+
# @return [CassandraComplex::Connection] new instance of Connection
|
|
41
|
+
def connection(kyspc=nil)
|
|
42
|
+
CassandraComplex::Connection.connection(kyspc || self.keyspace)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Execute code within specific keyspace
|
|
46
|
+
#
|
|
47
|
+
# @param [String] kyspc keyspace in which code should be executed
|
|
48
|
+
# @yieldparam [Proc] blck custom code to be executed within keyspace
|
|
49
|
+
def with_keyspace(kyspc, &blck)
|
|
50
|
+
connection.with_keyspace(kyspc, &blck)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Set table name for particular table
|
|
54
|
+
#
|
|
55
|
+
# @param[String] tbl table name
|
|
56
|
+
def set_table_name(tbl)
|
|
57
|
+
@tbl_name = tbl
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Return table name
|
|
61
|
+
#
|
|
62
|
+
# @return [String] table name
|
|
63
|
+
def table_name
|
|
64
|
+
@tbl_name || self.name.downcase
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Return first part of primary key
|
|
68
|
+
#
|
|
69
|
+
# @return [String] first part of primary key
|
|
70
|
+
def id
|
|
71
|
+
@id ||= connection.key_alias(table_name)
|
|
72
|
+
@id
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Return if table is empty
|
|
76
|
+
#
|
|
77
|
+
# @return [Boolean] if table is empty
|
|
78
|
+
def empty?
|
|
79
|
+
all.empty?
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Truncate table
|
|
83
|
+
# @return [Boolean] true
|
|
84
|
+
def truncate
|
|
85
|
+
command = "truncate #{table_name};"
|
|
86
|
+
rs = connection.execute(command, true, self)
|
|
87
|
+
true
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Raw query execution
|
|
91
|
+
# @param [String] cql_query_string
|
|
92
|
+
# @yieldparam [Proc] blck custom code to be executed within keyspace
|
|
93
|
+
# @return [Array<Hash>] result set as array of hashes
|
|
94
|
+
def execute(cql_query_string, &blck)
|
|
95
|
+
rs = connection.execute(cql_query_string, true, self, [], &blck)
|
|
96
|
+
rs
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Return result set for given primary key
|
|
100
|
+
# @see .build_select_clause
|
|
101
|
+
#
|
|
102
|
+
# @param [String, Array<String>, Hash] key key for where clause
|
|
103
|
+
# @param [Hash] clauses select clauses
|
|
104
|
+
# @option clauses [String, Array<String>] where where clause
|
|
105
|
+
# @option clauses [String] order order clause
|
|
106
|
+
# @option clauses [String] limit limit clause
|
|
107
|
+
# @return [Array<Hash>] array of hashes
|
|
108
|
+
def all(key=nil, clauses={}, &blck)
|
|
109
|
+
key = nil if key == :all
|
|
110
|
+
|
|
111
|
+
return_value = nil
|
|
112
|
+
if (!clauses[:select_expression])
|
|
113
|
+
if (clauses[:distinct])
|
|
114
|
+
clauses.merge!({:select_expression=>clauses[:distinct]})
|
|
115
|
+
else
|
|
116
|
+
clauses.merge!({:select_expression=>"*"})
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
command = build_select_clause(key, clauses)
|
|
121
|
+
if clauses[:where].kind_of?(Array)
|
|
122
|
+
bind = clauses[:where][1..-1]
|
|
123
|
+
else
|
|
124
|
+
bind = []
|
|
125
|
+
end
|
|
126
|
+
return_value = connection.execute(command, true, self, bind, &blck)
|
|
127
|
+
|
|
128
|
+
#distinct
|
|
129
|
+
return_value = return_value.map{|row| row[clauses[:distinct]]}.uniq if clauses[:distinct]
|
|
130
|
+
|
|
131
|
+
return_value
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
alias find all
|
|
135
|
+
|
|
136
|
+
# Return count of result set for given primary key
|
|
137
|
+
# @see .build_select_clause
|
|
138
|
+
#
|
|
139
|
+
# @param [String, Array<String>, Hash] key key for where clause
|
|
140
|
+
# @param [Hash] clauses select clauses
|
|
141
|
+
# @option clauses [String, Array<String>] where where clause
|
|
142
|
+
# @option clauses [String] order order clause
|
|
143
|
+
# @option clauses [String] limit limit clause
|
|
144
|
+
# @return [Array<Hash>] result as as array of hashes
|
|
145
|
+
def count(key=nil, clauses={}, &blck)
|
|
146
|
+
key = nil if key == :all
|
|
147
|
+
return_value = nil
|
|
148
|
+
command = build_select_clause(key, clauses.merge({:select_expression=>"count(1)"}))
|
|
149
|
+
if clauses[:where].kind_of?(Array)
|
|
150
|
+
bind = clauses[:where][1..-1]
|
|
151
|
+
else
|
|
152
|
+
bind = []
|
|
153
|
+
end
|
|
154
|
+
rs = connection.execute(command, true, self, bind, &blck)
|
|
155
|
+
if !rs.empty? && rs[0].has_key?('count')
|
|
156
|
+
return_value = rs[0]['count']
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
return_value
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# Insert record
|
|
163
|
+
# @param [Hash] clauses hash of fields, which need to be inserted
|
|
164
|
+
# @param [Hash] options create options, such as consistency
|
|
165
|
+
# @return [Boolean] always true
|
|
166
|
+
def create(clauses={}, options={})
|
|
167
|
+
return false if clauses.empty?
|
|
168
|
+
|
|
169
|
+
keys = clauses.keys.join(', ')
|
|
170
|
+
values = clauses.values.map{|x| !!options[:sanitize] ? x : CassandraCQL::Statement.quote(CassandraCQL::Statement.cast_to_cql(x))}.join(', ')
|
|
171
|
+
|
|
172
|
+
options_clause = ''
|
|
173
|
+
|
|
174
|
+
if !options.empty?
|
|
175
|
+
options_clause = "using " + options.map{|x,y| ' ' + x.to_s + ' ' + y.to_s + ' '}.join(' AND ')
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
command = "insert into #{table_name} (#{keys}) values (#{values}) #{options_clause}"
|
|
179
|
+
rs = connection.execute(command, true, self)
|
|
180
|
+
|
|
181
|
+
return true
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
alias update create
|
|
186
|
+
|
|
187
|
+
# Delete record(s)
|
|
188
|
+
# @param [String, Array<String>, Hash] key key for where clause
|
|
189
|
+
# @param [Hash] options options for delete
|
|
190
|
+
# @option options [String] timestamp timestamp of operation
|
|
191
|
+
# @option options [Array<String>] columns columns which should be deleted
|
|
192
|
+
# @option options [Array, String] where where options for delete operation
|
|
193
|
+
# @return [Boolean] always true
|
|
194
|
+
def delete(key=nil,options={})
|
|
195
|
+
return false unless (key || options.has_key?(:where))
|
|
196
|
+
|
|
197
|
+
where_clause = build_where_clause(key, options)
|
|
198
|
+
|
|
199
|
+
consistency_clause = ''
|
|
200
|
+
consistency_clause = " using consistency quorum and timestamp #{options[:timestamp]} " if options[:timestamp]
|
|
201
|
+
|
|
202
|
+
columns_clause = ''
|
|
203
|
+
columns_clause = options[:columns].join(', ') if options[:columns]
|
|
204
|
+
command = "delete #{columns_clause} from #{table_name} #{consistency_clause} #{where_clause}"
|
|
205
|
+
|
|
206
|
+
if options[:where].kind_of?(Array)
|
|
207
|
+
bind = options[:where][1..-1]
|
|
208
|
+
else
|
|
209
|
+
bind = []
|
|
210
|
+
end
|
|
211
|
+
rs = connection.execute(command, true, self, bind)
|
|
212
|
+
|
|
213
|
+
return true
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
protected
|
|
217
|
+
|
|
218
|
+
# Build select clause from clauses hash
|
|
219
|
+
# @see .build_where_clause
|
|
220
|
+
#
|
|
221
|
+
# @param [String, Array<String>, Hash] key key for where clause
|
|
222
|
+
# @param [Hash] clauses select clauses
|
|
223
|
+
# @option clauses [String, Array<String>] where where clause
|
|
224
|
+
# @option clauses [String] order order clause
|
|
225
|
+
# @option clauses [String] limit limit clause
|
|
226
|
+
# @return [String] CQL command
|
|
227
|
+
def build_select_clause(key=nil, clauses={})
|
|
228
|
+
where_clause = build_where_clause(key, clauses)
|
|
229
|
+
order_clause = ''
|
|
230
|
+
limit_clause = ''
|
|
231
|
+
if !clauses.empty?
|
|
232
|
+
order_clause = ' order by ' + clauses[:order] if clauses[:order]
|
|
233
|
+
limit_clause = ' limit ' + clauses[:limit].to_s if clauses[:limit]
|
|
234
|
+
end
|
|
235
|
+
command = "select #{clauses[:select_expression]} from #{table_name} #{where_clause} #{order_clause} #{limit_clause};"
|
|
236
|
+
command
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
# Build where clause from clauses hash
|
|
240
|
+
#
|
|
241
|
+
# @param [String, Array<String>, Hash] key key for where clause
|
|
242
|
+
# @param [Hash] clauses where clauses
|
|
243
|
+
# @option clauses [String, Array<String>] where where clause
|
|
244
|
+
# @return [String] where part of CQL command
|
|
245
|
+
def build_where_clause(key, clauses)
|
|
246
|
+
where_clause = ''
|
|
247
|
+
if clauses[:where].kind_of?(Array)
|
|
248
|
+
where = clauses[:where][0]
|
|
249
|
+
else
|
|
250
|
+
where = clauses[:where]
|
|
251
|
+
end
|
|
252
|
+
if key
|
|
253
|
+
if key.kind_of?(String)
|
|
254
|
+
where_clause = "where #{id} = #{CassandraCQL::Statement.quote(CassandraCQL::Statement.cast_to_cql(key))}"
|
|
255
|
+
elsif key.kind_of?(Array)
|
|
256
|
+
where_clause = "where #{id} in (#{key.map{|x| CassandraCQL::Statement.quote(CassandraCQL::Statement.cast_to_cql(x))}.join(', ')})"
|
|
257
|
+
elsif key.kind_of?(Hash)
|
|
258
|
+
where_clause = "where " + key.map{|x,y| "#{x} = #{CassandraCQL::Statement.quote(CassandraCQL::Statement.cast_to_cql(y))}"}.join(' and ')
|
|
259
|
+
end
|
|
260
|
+
if !clauses.empty? && clauses[:where]
|
|
261
|
+
where_clause << ' and ' + where
|
|
262
|
+
end
|
|
263
|
+
elsif !clauses.empty? && clauses[:where]
|
|
264
|
+
where_clause = 'where ' + where
|
|
265
|
+
end
|
|
266
|
+
where_clause
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
end
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
class TimelineModel < CassandraComplex::Model
|
|
4
|
+
table 'timeline'
|
|
5
|
+
|
|
6
|
+
attribute :user_id, 'varchar'
|
|
7
|
+
attribute :tweet_id, 'int'
|
|
8
|
+
attribute :author, 'varchar'
|
|
9
|
+
attribute :body, 'varchar'
|
|
10
|
+
|
|
11
|
+
primary_key :user_id, :tweet_id
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
class Tickets < CassandraComplex::Model
|
|
15
|
+
table 'tickets'
|
|
16
|
+
|
|
17
|
+
attribute :user_id, 'int'
|
|
18
|
+
attribute :owner, 'varchar'
|
|
19
|
+
attribute :time, 'timestamp'
|
|
20
|
+
|
|
21
|
+
primary_key :user_id
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
#CassandraComplex::Configuration.logger = Logger.new('/dev/null')
|
|
25
|
+
|
|
26
|
+
describe 'Model' do
|
|
27
|
+
|
|
28
|
+
before :all do
|
|
29
|
+
conn = CassandraComplex::Connection.new('127.0.0.1:9160')
|
|
30
|
+
conn.execute('CREATE KEYSPACE cassandra_complex_test WITH strategy_class = \'SimpleStrategy\' AND strategy_options:replication_factor = 1;')
|
|
31
|
+
CassandraComplex::Configuration.read({'host'=>'127.0.0.1:9160', 'default_keyspace'=>'cassandra_complex_test'})
|
|
32
|
+
TimelineModel.create_table
|
|
33
|
+
Tickets.create_table
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
after :all do
|
|
37
|
+
conn = CassandraComplex::Connection.new('127.0.0.1:9160')
|
|
38
|
+
TimelineModel.drop_table
|
|
39
|
+
Tickets.drop_table
|
|
40
|
+
conn.execute('DROP KEYSPACE cassandra_complex;')
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
context 'basic operations' do
|
|
44
|
+
before :each do
|
|
45
|
+
TimelineModel.truncate
|
|
46
|
+
Tickets.truncate
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it 'returns schema' do
|
|
50
|
+
TimelineModel.schema.should == {:table => 'timeline', :attributes=>{:user_id => 'varchar', :tweet_id => 'int', :author => 'varchar', :body => 'varchar'}, :primary_key => [:user_id, :tweet_id]}
|
|
51
|
+
Tickets.schema.should == {:table => 'tickets', :attributes=>{:user_id => 'int', :owner => 'varchar', :time => 'timestamp'}, :primary_key => [:user_id]}
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it 'checks equality of two models' do
|
|
55
|
+
timeline1 = TimelineModel.new({'user_id' => 'test_user1', 'tweet_id' => 1, 'author' => 'test_author1', 'body' => 'test_body1'})
|
|
56
|
+
timeline2 = TimelineModel.new({'user_id' => 'test_user1', 'tweet_id' => 1, 'author' => 'test_author1', 'body' => 'test_body1'})
|
|
57
|
+
|
|
58
|
+
timeline1.should == timeline2
|
|
59
|
+
|
|
60
|
+
timeline3 = TimelineModel.new({'user_id' => 'test_user3', 'tweet_id' => 3, 'author' => 'test_author3', 'body' => 'test_body3'})
|
|
61
|
+
timeline1.should_not == timeline3
|
|
62
|
+
|
|
63
|
+
tickets = Tickets.new({:user_id => 10, :owner=> 'Tim Collins', :time => Time.now})
|
|
64
|
+
|
|
65
|
+
tickets.should_not == timeline3
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it 'implements dirtiness' do
|
|
69
|
+
timeline1 = TimelineModel.new({'user_id' => 'test_user1', 'tweet_id' => 1, 'author' => 'test_author1', 'body' => 'test_body1'})
|
|
70
|
+
timeline1.dirty?.should == true
|
|
71
|
+
|
|
72
|
+
timeline1.save
|
|
73
|
+
timeline1.dirty?.should == false
|
|
74
|
+
|
|
75
|
+
timeline1 == TimelineModel.all[0]
|
|
76
|
+
timeline1.dirty?.should == false
|
|
77
|
+
timeline1.author = 'test_author42'
|
|
78
|
+
timeline1.dirty?.should == true
|
|
79
|
+
timeline1.save
|
|
80
|
+
|
|
81
|
+
timelines = TimelineModel.all
|
|
82
|
+
timelines.size.should == 1
|
|
83
|
+
timeline1 = timelines[0]
|
|
84
|
+
|
|
85
|
+
timeline1.user_id.should == 'test_user1'
|
|
86
|
+
timeline1.tweet_id.should == 1
|
|
87
|
+
timeline1.author.should == 'test_author42'
|
|
88
|
+
timeline1.body.should == 'test_body1'
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
context 'creating new model' do
|
|
93
|
+
before (:each) do
|
|
94
|
+
TimelineModel.truncate
|
|
95
|
+
Tickets.truncate
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
it 'with Hash' do
|
|
99
|
+
timeline1 = TimelineModel.new({'user_id' => 'test_user1', 'tweet_id' => 1, 'author' => 'test_author1', 'body' => 'test_body1'})
|
|
100
|
+
|
|
101
|
+
timeline1.user_id.should == 'test_user1'
|
|
102
|
+
timeline1.tweet_id.should == 1
|
|
103
|
+
timeline1.author.should == 'test_author1'
|
|
104
|
+
timeline1.body.should == 'test_body1'
|
|
105
|
+
|
|
106
|
+
ticket1 = Tickets.new({:user_id => 10, :owner=> 'Tim Collins', :time => Time.now})
|
|
107
|
+
|
|
108
|
+
ticket1.user_id.should == 10
|
|
109
|
+
ticket1.owner.should == 'Tim Collins'
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
it 'with assigment' do
|
|
113
|
+
timeline = TimelineModel.new
|
|
114
|
+
|
|
115
|
+
timeline.user_id = 'test_user2'
|
|
116
|
+
timeline.tweet_id = 2
|
|
117
|
+
timeline.author = 'test_author2'
|
|
118
|
+
timeline.body = 'test_body2'
|
|
119
|
+
|
|
120
|
+
timeline.user_id.should == 'test_user2'
|
|
121
|
+
timeline.tweet_id.should == 2
|
|
122
|
+
timeline.author.should == 'test_author2'
|
|
123
|
+
timeline.body.should == 'test_body2'
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
it 'creating and saving new model within DB' do
|
|
127
|
+
timeline_created = TimelineModel.create({'user_id' => 'test_user1', 'tweet_id' => 1, 'author' => 'test_author1', 'body' => 'test_body1'})
|
|
128
|
+
|
|
129
|
+
timeline_created.user_id.should == 'test_user1'
|
|
130
|
+
timeline_created.tweet_id.should == 1
|
|
131
|
+
timeline_created.author.should == 'test_author1'
|
|
132
|
+
timeline_created.body.should == 'test_body1'
|
|
133
|
+
|
|
134
|
+
timeline_found = TimelineModel.find('test_user1', :where=>['tweet_id = ?', 1])
|
|
135
|
+
timeline_created.should == timeline_found.first
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
context 'saving model' do
|
|
140
|
+
|
|
141
|
+
before (:each) do
|
|
142
|
+
TimelineModel.truncate
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
it 'just save' do
|
|
146
|
+
timeline = TimelineModel.new({'user_id' => 'test_user1', 'tweet_id' => 1, 'author' => 'test_author1', 'body' => 'test_body1'})
|
|
147
|
+
|
|
148
|
+
timeline.save
|
|
149
|
+
|
|
150
|
+
TimelineModel.all.size.should == 1
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
context 'deleting model' do
|
|
155
|
+
before (:each) do
|
|
156
|
+
TimelineModel.truncate
|
|
157
|
+
TimelineModel.create({'user_id' => 'test_user1', 'tweet_id' => 1, 'author' => 'test_author1', 'body' => 'test_body1'})
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
it 'as class method' do
|
|
161
|
+
TimelineModel.delete({'user_id' => 'test_user1', 'tweet_id' => 1})
|
|
162
|
+
TimelineModel.all.size.should == 0
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
it 'as instance method' do
|
|
166
|
+
TimelineModel.find('test_user1', :where=>['tweet_id = ?', 1])[0].delete
|
|
167
|
+
TimelineModel.all.size.should == 0
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
context 'all, find, count, etc' do
|
|
173
|
+
|
|
174
|
+
before (:each) do
|
|
175
|
+
TimelineModel.truncate
|
|
176
|
+
TimelineModel.create({'user_id' => 'test_user0', 'tweet_id' => 0, 'author' => 'test_author0', 'body' => 'test_body0'})
|
|
177
|
+
TimelineModel.create({'user_id' => 'test_user0', 'tweet_id' => 1, 'author' => 'test_author1', 'body' => 'test_body1'})
|
|
178
|
+
TimelineModel.create({'user_id' => 'test_user2', 'tweet_id' => 2, 'author' => 'test_author2', 'body' => 'test_body2'})
|
|
179
|
+
TimelineModel.create({'user_id' => 'test_user3', 'tweet_id' => 3, 'author' => 'test_author3', 'body' => 'test_body3'})
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
it 'count' do
|
|
184
|
+
TimelineModel.count.should == 4
|
|
185
|
+
TimelineModel.count('test_user0').should == 2
|
|
186
|
+
TimelineModel.count(:all, {:where=>['user_id = ? and tweet_id = ?', 'test_user0', 0]}).should == 1
|
|
187
|
+
TimelineModel.count({'user_id' => 'test_user0'}).should == 2
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
it 'all' do
|
|
191
|
+
TimelineModel.all.size.should == 4
|
|
192
|
+
TimelineModel.all({:where=>['user_id = ?', 'test_user0']}).size.should == 2
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
it 'find' do
|
|
196
|
+
TimelineModel.find(:all).size.should == 4
|
|
197
|
+
TimelineModel.find(:all, {:where=>['user_id = ?', 'test_user0']}).size.should == 2
|
|
198
|
+
TimelineModel.find({'user_id' => 'test_user0', 'tweet_id' => 1}).size.should == 1
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
end
|