redis-objects 1.7.0 → 2.0.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.
data/lib/redis/list.rb CHANGED
@@ -31,11 +31,7 @@ class Redis
31
31
  # Remove a member from the end of the list. Redis: RPOP
32
32
  def pop(n=nil)
33
33
  if n
34
- result, = redis.multi do
35
- redis.lrange(key, -n, -1)
36
- redis.ltrim(key, 0, -n - 1)
37
- end
38
- unmarshal result
34
+ unmarshal redis.rpop(key, n)
39
35
  else
40
36
  unmarshal redis.rpop(key)
41
37
  end
@@ -65,11 +61,7 @@ class Redis
65
61
  # Remove a member from the start of the list. Redis: LPOP
66
62
  def shift(n=nil)
67
63
  if n
68
- result, = redis.multi do
69
- redis.lrange(key, 0, n - 1)
70
- redis.ltrim(key, n, -1)
71
- end
72
- unmarshal result
64
+ unmarshal redis.lpop(key, n)
73
65
  else
74
66
  unmarshal redis.lpop(key)
75
67
  end
@@ -14,7 +14,7 @@ class Redis
14
14
  module ClassMethods
15
15
  # Define a new lock. It will function like a model attribute,
16
16
  # so it can be used alongside ActiveRecord/DataMapper, etc.
17
- def lock(name, options={})
17
+ def redis_lock(name, options={})
18
18
  options[:timeout] ||= 5 # seconds
19
19
  lock_name = "#{name}_lock"
20
20
  redis_objects[lock_name.to_sym] = options.merge(:type => :lock)
@@ -1,5 +1,5 @@
1
1
  class Redis
2
2
  module Objects
3
- VERSION = "1.7.0"
3
+ VERSION = "2.0.0"
4
4
  end
5
5
  end
data/lib/redis/objects.rb CHANGED
@@ -62,10 +62,23 @@ class Redis
62
62
  @redis = Objects::ConnectionPoolProxy.proxy_if_needed(conn)
63
63
  end
64
64
  def redis
65
- @redis || $redis || Redis.current ||
65
+ @redis || $redis ||
66
66
  raise(NotConnected, "Redis::Objects.redis not set to a Redis.new connection")
67
67
  end
68
68
 
69
+ # Toggles whether to use the legacy redis key naming scheme, which causes
70
+ # naming conflicts in certain cases.
71
+ # (attr_accessor with a default value)
72
+ attr_writer :prefix_style
73
+ def prefix_style
74
+ # NOTE: In a future release the default will change to :modern
75
+ @prefix_style ||= :legacy
76
+ end
77
+
78
+ def redis_legacy_naming?
79
+ prefix_style == :legacy
80
+ end
81
+
69
82
  def included(klass)
70
83
  # Core (this file)
71
84
  klass.instance_variable_set(:@redis, nil)
@@ -101,16 +114,115 @@ class Redis
101
114
  @redis_objects ||= {}
102
115
  end
103
116
 
104
- # Set the Redis redis_prefix to use. Defaults to model_name
105
- def redis_prefix=(redis_prefix) @redis_prefix = redis_prefix end
117
+ # Set the Redis redis_prefix to use. Defaults to class_name.
118
+ def redis_prefix=(redis_prefix)
119
+ @silence_warnings_as_redis_prefix_was_set_manually = true
120
+ @redis_prefix = redis_prefix
121
+ end
122
+
106
123
  def redis_prefix(klass = self) #:nodoc:
107
- @redis_prefix ||= klass.name.to_s.
108
- sub(%r{(.*::)}, '').
109
- gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
110
- gsub(/([a-z\d])([A-Z])/,'\1_\2').
124
+ @redis_prefix ||=
125
+ if Objects.redis_legacy_naming?
126
+ redis_legacy_naming_warning_message(klass)
127
+ redis_legacy_prefix(klass)
128
+ else
129
+ redis_modern_prefix(klass)
130
+ end
131
+
132
+ @redis_prefix
133
+ end
134
+
135
+ def redis_modern_prefix(klass = self) #:nodoc:
136
+ klass.name.to_s.
137
+ gsub(/::/, '__'). # Nested::Class => Nested__Class
138
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2'). # ClassName => Class_Name
139
+ gsub(/([a-z\d])([A-Z])/,'\1_\2'). # className => class_Name
140
+ downcase
141
+ end
142
+
143
+ def redis_legacy_prefix(klass = self) #:nodoc:
144
+ klass.name.to_s.
145
+ sub(%r{(.*::)}, ''). # Nested::Class => Class (problematic)
146
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2'). # ClassName => Class_Name
147
+ gsub(/([a-z\d])([A-Z])/,'\1_\2'). # className => class_Name
111
148
  downcase
112
149
  end
113
150
 
151
+ attr_accessor :redis_silence_warnings
152
+
153
+ # Temporary warning to help with migrating key names
154
+ def redis_legacy_naming_warning_message(klass)
155
+ # warn @silence_warnings_as_redis_prefix_was_set_manually.inspect
156
+ return if redis_silence_warnings || @silence_warnings_as_redis_prefix_was_set_manually
157
+
158
+ modern = redis_modern_prefix(klass)
159
+ legacy = redis_legacy_prefix(klass)
160
+ return if modern == legacy
161
+
162
+ warn <<EOW
163
+
164
+ [redis-objects] WARNING: redis-objects 2.0.0, revises key naming to fix a longstanding bug.
165
+ [redis-objects] Your class #{klass.name} should be updated to resolve this bug!
166
+ [redis-objects] Current key prefix: #{legacy.inspect}
167
+ [redis-objects] Future key prefix: #{modern.inspect}
168
+ [redis-objects] Read more at https://github.com/nateware/redis-objects/issues/231
169
+ EOW
170
+ end
171
+
172
+ # To be run once per Redis::Objects enhanced model
173
+ def migrate_redis_legacy_keys(scan_count=10, verbose=false)
174
+ unless Objects.redis_legacy_naming?
175
+ raise "Redis::Objects is already configured to use modern key prefixes."
176
+ end
177
+
178
+ legacy = redis_legacy_prefix
179
+ modern = redis_modern_prefix
180
+ if modern == legacy
181
+ warn "[redis-objects] #{self.name}.#{__method__} NOOP. Legacy and modern redis_prefix are the same (#{modern})"
182
+ return
183
+ end
184
+
185
+ warn "\n[redis-objects] Migrating keys from '#{legacy}' prefix to '#{modern}'"
186
+
187
+ cursor = 0
188
+ total_keys = 0
189
+
190
+ # Temporarily update the prefix to modern while we update these keys.
191
+ # NOTE: we cannot simply adjust the prefix_style, because the prefix is cached by @redis_prefix
192
+ self.redis_prefix = modern
193
+
194
+ loop do
195
+ cursor, keys = redis.scan(cursor, :match => "#{legacy}:*", :count => scan_count)
196
+ # REM: scan returns keys in a randomized order
197
+ keys.sort!
198
+ #puts "got #{keys.length} keys"
199
+ total_keys += keys.length
200
+ keys.each do |key|
201
+ # Split key name apart on ':'
202
+ # REM: global keys will have an empty id
203
+ # klass::object_accessor_name
204
+ _base_class, id, name = key.split(':')
205
+
206
+ # Figure out the new name
207
+ new_key = redis_field_key(name, id)
208
+
209
+ # Rename the key
210
+ if verbose
211
+ warn "[redis-objects] Rename '#{key}', '#{new_key}'"
212
+ end
213
+ ok = redis.rename(key, new_key)
214
+ warn "[redis-objects] Warning: Rename '#{key}', '#{new_key}' failed: #{ok}" if ok != 'OK'
215
+ end
216
+ break if cursor == "0"
217
+ end
218
+
219
+ warn "[redis-objects] Migrated #{total_keys} total number of redis keys"
220
+
221
+ ensure
222
+ # Change the prefix back (just in case)
223
+ self.redis_prefix = legacy
224
+ end
225
+
114
226
  def redis_options(name)
115
227
  klass = first_ancestor_with(name)
116
228
  return klass.redis_objects[name.to_sym] || {}
@@ -130,18 +242,24 @@ class Redis
130
242
  klass = first_ancestor_with(name)
131
243
  # READ THIS: This can never ever ever ever change or upgrades will corrupt all data
132
244
  # I don't think people were using Proc as keys before (that would create a weird key). Should be ok
245
+
246
+ # If a custom key was set for this accessor
133
247
  if key = klass.redis_objects[name.to_sym][:key]
248
+ # If that custom key was callable (E.G. a proc)
134
249
  if key.respond_to?(:call)
135
250
  key = key.call context
136
251
  else
137
252
  context.instance_eval "%(#{key})"
138
253
  end
254
+
139
255
  else
256
+ # If its not a global key, and ID is nil, then throw an error
140
257
  if id.nil? and !klass.redis_objects[name.to_sym][:global]
141
258
  raise NilObjectId,
142
259
  "[#{klass.redis_objects[name.to_sym]}] Attempt to address redis-object " +
143
260
  ":#{name} on class #{klass.name} with nil id (unsaved record?) [object_id=#{object_id}]"
144
261
  end
262
+ # Otherwise return the constructed key
145
263
  "#{redis_prefix(klass)}:#{id}:#{name}"
146
264
  end
147
265
  end
data/lib/redis/set.rb CHANGED
@@ -15,7 +15,8 @@ class Redis
15
15
  # Redis: SADD
16
16
  def add(value)
17
17
  allow_expiration do
18
- redis.sadd(key, marshal(value)) if value.nil? || !Array(value).empty?
18
+ value = '' if value.nil?
19
+ redis.sadd(key, marshal(value)) if !Array(value).empty? # allow empty adds
19
20
  end
20
21
  end
21
22
 
@@ -211,7 +211,7 @@ class Redis
211
211
  result = redis.zrange(temp_key, 0, -1)
212
212
  end
213
213
 
214
- result.value
214
+ result
215
215
  end
216
216
  alias_method :intersect, :intersection
217
217
  alias_method :inter, :intersection
@@ -248,7 +248,7 @@ class Redis
248
248
  result = redis.zrange(temp_key, 0, -1)
249
249
  end
250
250
 
251
- result.value
251
+ result
252
252
  end
253
253
  alias_method :|, :union
254
254
  alias_method :+, :union
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ["lib"]
20
20
 
21
21
  # Only fix this one version or else tests break
22
- spec.add_dependency "redis"
22
+ spec.add_dependency "redis", '~> 5.0'
23
23
 
24
24
  # Ignore gemspec warnings on these. Trying to fix them to versions breaks TravisCI
25
25
  spec.add_development_dependency "bundler"
@@ -0,0 +1,427 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ require 'redis/objects'
4
+ Redis::Objects.redis = REDIS_HANDLE
5
+
6
+ require 'securerandom'
7
+
8
+ require "stringio"
9
+
10
+ def capture_stderr
11
+ # The output stream must be an IO-like object. In this case we capture it in
12
+ # an in-memory IO object so we can return the string value. You can assign any
13
+ # IO object here.
14
+ previous_stderr, $stderr = $stderr, StringIO.new
15
+ yield
16
+ $stderr.string
17
+ ensure
18
+ # Restore the previous value of stderr (typically equal to STDERR).
19
+ $stderr = previous_stderr
20
+ end
21
+
22
+ describe 'Redis key prefix naming compatibility' do
23
+
24
+ describe 'verifies single level classes' do # context
25
+
26
+ it 'work the same (modern)' do
27
+ Redis::Objects.prefix_style = :modern
28
+
29
+ class SingleLevelOne
30
+ include Redis::Objects
31
+
32
+ def id
33
+ 1
34
+ end
35
+ end
36
+
37
+ obj = SingleLevelOne.new
38
+ obj.class.redis_prefix.should == 'single_level_one'
39
+ end
40
+
41
+ it 'work the same (legacy)' do
42
+ Redis::Objects.prefix_style = :legacy
43
+
44
+ class SingleLevelTwo
45
+ include Redis::Objects
46
+
47
+ def id
48
+ 1
49
+ end
50
+ end
51
+
52
+ obj = SingleLevelTwo.new
53
+ obj.class.redis_prefix.should == 'single_level_two'
54
+ end
55
+
56
+ end # context
57
+
58
+ describe 'verifies nested classes' do # context
59
+
60
+ it 'do NOT work the same (modern)' do
61
+ Redis::Objects.prefix_style = :modern
62
+
63
+ module Nested
64
+ class NamingOne
65
+ include Redis::Objects
66
+
67
+ def id
68
+ 1
69
+ end
70
+ end
71
+ end
72
+
73
+ obj = Nested::NamingOne.new
74
+ obj.class.redis_prefix.should == 'nested__naming_one'
75
+ end
76
+
77
+ it 'do NOT work the same (legacy)' do
78
+ Redis::Objects.prefix_style = :legacy
79
+
80
+ module Nested
81
+ class NamingTwo
82
+ include Redis::Objects
83
+ self.redis_silence_warnings = true
84
+
85
+ def id
86
+ 1
87
+ end
88
+ end
89
+ end
90
+
91
+ obj = Nested::NamingTwo.new
92
+ obj.class.redis_prefix.should == 'naming_two'
93
+ end
94
+
95
+ end # context
96
+
97
+ describe 'verifies that multiple levels' do # context
98
+
99
+ it 'respect __ vs _' do
100
+ Redis::Objects.prefix_style = :modern
101
+
102
+ module NestedLevel
103
+ module Further
104
+ class NamingThree
105
+ include Redis::Objects
106
+
107
+ def id
108
+ 1
109
+ end
110
+ end
111
+ end
112
+ end
113
+
114
+ obj = NestedLevel::Further::NamingThree.new
115
+ obj.class.redis_prefix.should == 'nested_level__further__naming_three'
116
+ end
117
+
118
+ it 'respect legacy naming' do
119
+ Redis::Objects.prefix_style = :legacy
120
+
121
+ module NestedLevel
122
+ module Further
123
+ class NamingFour
124
+ include Redis::Objects
125
+ self.redis_silence_warnings = true
126
+
127
+ def id
128
+ 1
129
+ end
130
+
131
+ redis_handle = Redis.new(:host => REDIS_HOST, :port => REDIS_PORT, :db => 31)
132
+ value :redis_value, :redis => redis_handle
133
+ end
134
+ end
135
+ end
136
+
137
+ obj = NestedLevel::Further::NamingFour.new
138
+ obj.class.redis_prefix.should == 'naming_four'
139
+ val = SecureRandom.hex(10)
140
+ obj.redis_value = val
141
+ obj.redis_value.should == val
142
+ obj.redis_value.key.should == 'naming_four:1:redis_value'
143
+ end
144
+
145
+ it 'do not conflict 1' do
146
+ Redis::Objects.prefix_style = :modern
147
+
148
+ module NestedLevel
149
+ module Further
150
+ class NamingFive
151
+ include Redis::Objects
152
+
153
+ def id
154
+ 1
155
+ end
156
+
157
+ value :redis_value
158
+ end
159
+ end
160
+ end
161
+
162
+ obj = NestedLevel::Further::NamingFive.new
163
+ obj.class.redis_prefix.should == 'nested_level__further__naming_five'
164
+ val = SecureRandom.hex(10)
165
+ obj.redis_value = val
166
+ obj.redis_value.should == val
167
+ obj.redis_value.key.should == 'nested_level__further__naming_five:1:redis_value'
168
+ obj.redis_value.redis.should == obj.redis
169
+ obj.redis.get('nested_level__further__naming_five:1:redis_value').should == val
170
+ end
171
+
172
+ it 'do not conflict 2' do
173
+ Redis::Objects.prefix_style = :modern
174
+
175
+ module Nested
176
+ module LevelFurtherNaming
177
+ class Five
178
+ include Redis::Objects
179
+
180
+ def id
181
+ 1
182
+ end
183
+
184
+ value :redis_value
185
+ end
186
+ end
187
+ end
188
+
189
+ obj = Nested::LevelFurtherNaming::Five.new
190
+ obj.class.redis_prefix.should == 'nested__level_further_naming__five'
191
+ val = SecureRandom.hex(10)
192
+ obj.redis_value = val
193
+ obj.redis_value.should == val
194
+ obj.redis_value.key.should == 'nested__level_further_naming__five:1:redis_value'
195
+ obj.redis.get('nested__level_further_naming__five:1:redis_value').should == val
196
+ end
197
+
198
+ it 'do not conflict 3' do
199
+ Redis::Objects.prefix_style = :modern
200
+
201
+ module Nested
202
+ module LevelFurther
203
+ class NamingFive
204
+ include Redis::Objects
205
+
206
+ def id
207
+ 1
208
+ end
209
+
210
+ value :redis_value
211
+ end
212
+ end
213
+ end
214
+
215
+ obj = Nested::LevelFurther::NamingFive.new
216
+ obj.class.redis_prefix.should == 'nested__level_further__naming_five'
217
+ val = SecureRandom.hex(10)
218
+ obj.redis_value = val
219
+ obj.redis_value.should == val
220
+ obj.redis_value.key.should == 'nested__level_further__naming_five:1:redis_value'
221
+ obj.redis.get('nested__level_further__naming_five:1:redis_value').should == val
222
+ end
223
+
224
+ end # context
225
+
226
+ describe 'handles dynamically created classes correctly' do # context
227
+
228
+ it 'in modern mode' do
229
+ Redis::Objects.prefix_style = :modern
230
+
231
+ module Nested
232
+ class LevelSix
233
+ include Redis::Objects
234
+
235
+ def id
236
+ 1
237
+ end
238
+
239
+ value :redis_value
240
+ end
241
+ end
242
+
243
+ obj = Nested::LevelSix.new
244
+ obj.class.redis_prefix.should == 'nested__level_six'
245
+ val = SecureRandom.hex(10)
246
+ obj.redis_value = val
247
+ obj.redis_value.should == val
248
+ obj.redis_value.key.should == 'nested__level_six:1:redis_value'
249
+ obj.redis.get('nested__level_six:1:redis_value').should == val
250
+
251
+ DynamicClass = Class.new(Nested::LevelSix)
252
+ DynamicClass.value :redis_value2
253
+ obj2 = DynamicClass.new
254
+ DynamicClass.redis_prefix.should == 'dynamic_class'
255
+ obj2.redis_value.should.be.kind_of(Redis::Value)
256
+ obj2.redis_value2.should.be.kind_of(Redis::Value)
257
+ obj2.redis_value.key.should == 'dynamic_class:1:redis_value'
258
+ obj2.redis_value2.key.should == 'dynamic_class:1:redis_value2'
259
+
260
+ end
261
+
262
+ it 'in legacy mode' do
263
+ Redis::Objects.prefix_style = :legacy
264
+
265
+ module Nested
266
+ class LevelSeven
267
+ include Redis::Objects
268
+ self.redis_silence_warnings = true
269
+
270
+ def id
271
+ 1
272
+ end
273
+
274
+ value :redis_value
275
+ end
276
+ end
277
+
278
+ obj = Nested::LevelSeven.new
279
+ obj.class.redis_prefix.should == 'level_seven'
280
+ val = SecureRandom.hex(10)
281
+ obj.redis_value = val
282
+ obj.redis_value.should == val
283
+ obj.redis_value.key.should == 'level_seven:1:redis_value'
284
+ obj.redis.get('level_seven:1:redis_value').should == val
285
+
286
+ DynamicClass2 = Class.new(Nested::LevelSeven)
287
+ DynamicClass2.value :redis_value2
288
+ obj2 = DynamicClass2.new
289
+ DynamicClass2.redis_prefix.should == 'dynamic_class2'
290
+ obj2.redis_value.should.be.kind_of(Redis::Value)
291
+ obj2.redis_value2.should.be.kind_of(Redis::Value)
292
+ obj2.redis_value.key.should == 'dynamic_class2:1:redis_value'
293
+ obj2.redis_value2.key.should == 'dynamic_class2:1:redis_value2'
294
+ end
295
+
296
+ end # context
297
+
298
+ # ---- other tests ----
299
+
300
+ it 'prints a warning message if the key name changes' do
301
+ Redis::Objects.prefix_style = :legacy
302
+
303
+ module Nested
304
+ class LevelNine
305
+ include Redis::Objects
306
+
307
+ def id
308
+ 1
309
+ end
310
+
311
+ value :redis_value
312
+ end
313
+ end
314
+
315
+ captured_output = capture_stderr do
316
+ # Does not output anything directly.
317
+ obj = Nested::LevelNine.new
318
+ val = SecureRandom.hex(10)
319
+ obj.redis_value = val
320
+ obj.redis_value.should == val
321
+ end
322
+
323
+ captured_output.should =~ /Warning:/i
324
+ end
325
+
326
+ it 'supports a method to migrate legacy key names' do
327
+
328
+ module Nested
329
+ def self.make_class
330
+ # TODO notes
331
+ klass = Class.new do
332
+ def initialize(id)
333
+ @id = id
334
+ end
335
+ def id
336
+ @id
337
+ end
338
+
339
+ include Redis::Objects
340
+
341
+ self.redis_silence_warnings = true
342
+
343
+ value :redis_value
344
+ counter :redis_counter
345
+ hash_key :redis_hash
346
+ list :redis_list
347
+ set :redis_set
348
+ sorted_set :redis_sorted_set
349
+
350
+ # global class counters
351
+ value :global_value, :global => true
352
+ counter :global_counter, :global => true
353
+ hash_key :global_hash_key, :global => true
354
+ list :global_list, :global => true
355
+ set :global_set, :global => true
356
+ sorted_set :global_sorted_set, :global => true
357
+
358
+ # use a callable as the key
359
+ value :global_proc_value, :global => true, :key => Proc.new { |roster| "#{roster.name}:#{Time.now.strftime('%Y-%m-%dT%H')}:daily" }
360
+ end
361
+ end
362
+ end
363
+
364
+ # First define the class using legacy prefix
365
+ Redis::Objects.prefix_style = :legacy
366
+ Nested::UpgradeTest = Nested.make_class
367
+
368
+ # Sanity checks
369
+ Nested::UpgradeTest.redis_objects.length.should == 13
370
+ Nested::UpgradeTest.redis_prefix.should == 'upgrade_test'
371
+
372
+ # Create a whole bunch of keys using the legacy prefixed keys
373
+
374
+ 30.times do |i|
375
+ # warn i.inspect
376
+ obj = Nested::UpgradeTest.new(i)
377
+ obj.redis_value = i
378
+ obj.redis_counter.increment
379
+ obj.redis_hash[:key] = i
380
+ obj.redis_list << i
381
+ obj.redis_set << i
382
+ obj.redis_sorted_set[i] = i
383
+ end
384
+
385
+ obj = Nested::UpgradeTest.new(99)
386
+ obj.global_value = 42
387
+ obj.global_counter.increment
388
+ obj.global_counter.increment
389
+ obj.global_counter.increment
390
+ obj.global_hash_key[:key] = 'value'
391
+ obj.global_set << 'a' << 'b'
392
+ obj.global_sorted_set[:key] = 2.2
393
+
394
+ # Run the upgrade
395
+ Nested::UpgradeTest.migrate_redis_legacy_keys(1000)
396
+
397
+ # Re-Create the class using modern prefix
398
+ Nested.send(:remove_const, :UpgradeTest)
399
+ Redis::Objects.prefix_style = :modern
400
+ Nested::UpgradeTest = Nested.make_class
401
+
402
+ # Sanity checks
403
+ Nested::UpgradeTest.redis_objects.length.should == 13
404
+ Nested::UpgradeTest.redis_prefix.should == 'nested__upgrade_test'
405
+
406
+ # Try to access the keys through modern prefixed keys now
407
+ 30.times do |i|
408
+ # warn i.inspect
409
+ obj = Nested::UpgradeTest.new(i)
410
+ obj.redis_value.to_i.should == i
411
+ obj.redis_counter.to_i.should == 1
412
+ obj.redis_hash[:key].to_i.should == i
413
+ obj.redis_list[0].to_i.should == i
414
+ obj.redis_set.include?(i).should == true
415
+ obj.redis_sorted_set[i].should == i
416
+ end
417
+
418
+ obj = Nested::UpgradeTest.new(99)
419
+ obj.global_value.to_i.should == 42
420
+ obj.global_counter.to_i.should == 3
421
+ obj.global_hash_key[:key].should == 'value'
422
+ obj.global_set.include?('a').should == true
423
+ obj.global_set.include?('b').should == true
424
+ obj.global_sorted_set[:key].should == 2.2
425
+ end
426
+
427
+ end
@@ -44,7 +44,7 @@ begin
44
44
  def self.up
45
45
  create_table :posts do |t|
46
46
  t.string :title
47
- t.string :description, :length => 200
47
+ t.string :description, :limit => 200
48
48
  t.integer :total
49
49
  t.integer :blog_id
50
50
  t.timestamps null: true