rdp-mysql2 0.2.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. data/.gitignore +12 -0
  2. data/.rspec +2 -0
  3. data/.rvmrc +1 -0
  4. data/CHANGELOG.md +142 -0
  5. data/Gemfile +3 -0
  6. data/MIT-LICENSE +20 -0
  7. data/README.rdoc +261 -0
  8. data/Rakefile +5 -0
  9. data/benchmark/active_record.rb +51 -0
  10. data/benchmark/active_record_threaded.rb +42 -0
  11. data/benchmark/allocations.rb +33 -0
  12. data/benchmark/escape.rb +36 -0
  13. data/benchmark/query_with_mysql_casting.rb +80 -0
  14. data/benchmark/query_without_mysql_casting.rb +47 -0
  15. data/benchmark/sequel.rb +37 -0
  16. data/benchmark/setup_db.rb +119 -0
  17. data/benchmark/threaded.rb +44 -0
  18. data/examples/eventmachine.rb +21 -0
  19. data/examples/threaded.rb +20 -0
  20. data/ext/mysql2/client.c +839 -0
  21. data/ext/mysql2/client.h +41 -0
  22. data/ext/mysql2/extconf.rb +72 -0
  23. data/ext/mysql2/mysql2_ext.c +12 -0
  24. data/ext/mysql2/mysql2_ext.h +42 -0
  25. data/ext/mysql2/result.c +488 -0
  26. data/ext/mysql2/result.h +20 -0
  27. data/lib/active_record/connection_adapters/em_mysql2_adapter.rb +64 -0
  28. data/lib/active_record/connection_adapters/mysql2_adapter.rb +654 -0
  29. data/lib/active_record/fiber_patches.rb +104 -0
  30. data/lib/arel/engines/sql/compilers/mysql2_compiler.rb +11 -0
  31. data/lib/mysql2.rb +16 -0
  32. data/lib/mysql2/client.rb +240 -0
  33. data/lib/mysql2/em.rb +37 -0
  34. data/lib/mysql2/em_fiber.rb +31 -0
  35. data/lib/mysql2/error.rb +15 -0
  36. data/lib/mysql2/result.rb +5 -0
  37. data/lib/mysql2/version.rb +3 -0
  38. data/mysql2.gemspec +32 -0
  39. data/spec/em/em_fiber_spec.rb +22 -0
  40. data/spec/em/em_spec.rb +49 -0
  41. data/spec/mysql2/client_spec.rb +430 -0
  42. data/spec/mysql2/error_spec.rb +69 -0
  43. data/spec/mysql2/result_spec.rb +333 -0
  44. data/spec/rcov.opts +3 -0
  45. data/spec/spec_helper.rb +66 -0
  46. data/tasks/benchmarks.rake +20 -0
  47. data/tasks/compile.rake +71 -0
  48. data/tasks/rspec.rake +16 -0
  49. data/tasks/vendor_mysql.rake +40 -0
  50. metadata +236 -0
@@ -0,0 +1,104 @@
1
+ # Necessary monkeypatching to make AR fiber-friendly.
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+
6
+ def self.fiber_pools
7
+ @fiber_pools ||= []
8
+ end
9
+ def self.register_fiber_pool(fp)
10
+ fiber_pools << fp
11
+ end
12
+
13
+ class FiberedMonitor
14
+ class Queue
15
+ def initialize
16
+ @queue = []
17
+ end
18
+
19
+ def wait(timeout)
20
+ t = timeout || 5
21
+ fiber = Fiber.current
22
+ x = EM::Timer.new(t) do
23
+ @queue.delete(fiber)
24
+ fiber.resume(false)
25
+ end
26
+ @queue << fiber
27
+ Fiber.yield.tap do
28
+ x.cancel
29
+ end
30
+ end
31
+
32
+ def signal
33
+ fiber = @queue.pop
34
+ fiber.resume(true) if fiber
35
+ end
36
+ end
37
+
38
+ def synchronize
39
+ yield
40
+ end
41
+
42
+ def new_cond
43
+ Queue.new
44
+ end
45
+ end
46
+
47
+ # ActiveRecord's connection pool is based on threads. Since we are working
48
+ # with EM and a single thread, multiple fiber design, we need to provide
49
+ # our own connection pool that keys off of Fiber.current so that different
50
+ # fibers running in the same thread don't try to use the same connection.
51
+ class ConnectionPool
52
+ def initialize(spec)
53
+ @spec = spec
54
+
55
+ # The cache of reserved connections mapped to threads
56
+ @reserved_connections = {}
57
+
58
+ # The mutex used to synchronize pool access
59
+ @connection_mutex = FiberedMonitor.new
60
+ @queue = @connection_mutex.new_cond
61
+
62
+ # default 5 second timeout unless on ruby 1.9
63
+ @timeout = spec.config[:wait_timeout] || 5
64
+
65
+ # default max pool size to 5
66
+ @size = (spec.config[:pool] && spec.config[:pool].to_i) || 5
67
+
68
+ @connections = []
69
+ @checked_out = []
70
+ end
71
+
72
+ def clear_stale_cached_connections!
73
+ cache = @reserved_connections
74
+ keys = Set.new(cache.keys)
75
+
76
+ ActiveRecord::ConnectionAdapters.fiber_pools.each do |pool|
77
+ pool.busy_fibers.each_pair do |object_id, fiber|
78
+ keys.delete(object_id)
79
+ end
80
+ end
81
+
82
+ keys.each do |key|
83
+ next unless cache.has_key?(key)
84
+ checkin cache[key]
85
+ cache.delete(key)
86
+ end
87
+ end
88
+
89
+ private
90
+
91
+ def current_connection_id #:nodoc:
92
+ Fiber.current.object_id
93
+ end
94
+
95
+ def checkout_and_verify(c)
96
+ @checked_out << c
97
+ c.run_callbacks :checkout
98
+ c.verify!
99
+ c
100
+ end
101
+ end
102
+
103
+ end
104
+ end
@@ -0,0 +1,11 @@
1
+ module Arel
2
+ module SqlCompiler
3
+ class Mysql2Compiler < GenericCompiler
4
+ def limited_update_conditions(conditions, taken)
5
+ conditions << " LIMIT #{taken}"
6
+ conditions
7
+ end
8
+ end
9
+ end
10
+ end
11
+
@@ -0,0 +1,16 @@
1
+ # encoding: UTF-8
2
+ require 'date'
3
+ require 'bigdecimal'
4
+ require 'rational' unless RUBY_VERSION >= '1.9.2'
5
+
6
+ require 'mysql2/version' unless defined? Mysql2::VERSION
7
+ require 'mysql2/error'
8
+ require 'mysql2/mysql2'
9
+ require 'mysql2/client'
10
+ require 'mysql2/result'
11
+
12
+ # = Mysql2
13
+ #
14
+ # A modern, simple and very fast Mysql library for Ruby - binding to libmysql
15
+ module Mysql2
16
+ end
@@ -0,0 +1,240 @@
1
+ module Mysql2
2
+ class Client
3
+ attr_reader :query_options
4
+ @@default_query_options = {
5
+ :as => :hash, # the type of object you want each row back as; also supports :array (an array of values)
6
+ :async => false, # don't wait for a result after sending the query, you'll have to monitor the socket yourself then eventually call Mysql2::Client#async_result
7
+ :cast_booleans => false, # cast tinyint(1) fields as true/false in ruby
8
+ :symbolize_keys => false, # return field names as symbols instead of strings
9
+ :database_timezone => :local, # timezone Mysql2 will assume datetime objects are stored in
10
+ :application_timezone => nil, # timezone Mysql2 will convert to before handing the object back to the caller
11
+ :cache_rows => true, # tells Mysql2 to use it's internal row cache for results
12
+ :connect_flags => REMEMBER_OPTIONS | LONG_PASSWORD | LONG_FLAG | TRANSACTIONS | PROTOCOL_41 | SECURE_CONNECTION
13
+ }
14
+
15
+ def initialize(opts = {})
16
+ @query_options = @@default_query_options.dup
17
+
18
+ init_connection
19
+
20
+ [:reconnect, :connect_timeout].each do |key|
21
+ next unless opts.key?(key)
22
+ send(:"#{key}=", opts[key])
23
+ end
24
+ # force the encoding to utf8
25
+ self.charset_name = opts[:encoding] || 'utf8'
26
+
27
+ @read_timeout = opts[:read_timeout]
28
+ if @read_timeout and @read_timeout < 0
29
+ raise Mysql2::Error, "read_timeout must be a positive integer, you passed #{@read_timeout}"
30
+ end
31
+
32
+ ssl_set(*opts.values_at(:sslkey, :sslcert, :sslca, :sslcapath, :sslciper))
33
+
34
+ user = opts[:username]
35
+ pass = opts[:password]
36
+ host = opts[:host] || 'localhost'
37
+ port = opts[:port] || 3306
38
+ database = opts[:database]
39
+ socket = opts[:socket]
40
+ flags = opts[:flags] ? opts[:flags] | @query_options[:connect_flags] : @query_options[:connect_flags]
41
+
42
+ connect user, pass, host, port, database, socket, flags
43
+ end
44
+
45
+ def self.default_query_options
46
+ @@default_query_options
47
+ end
48
+
49
+ # NOTE: from ruby-mysql
50
+ if defined? Encoding
51
+ CHARSET_MAP = {
52
+ "armscii8" => nil,
53
+ "ascii" => Encoding::US_ASCII,
54
+ "big5" => Encoding::Big5,
55
+ "binary" => Encoding::ASCII_8BIT,
56
+ "cp1250" => Encoding::Windows_1250,
57
+ "cp1251" => Encoding::Windows_1251,
58
+ "cp1256" => Encoding::Windows_1256,
59
+ "cp1257" => Encoding::Windows_1257,
60
+ "cp850" => Encoding::CP850,
61
+ "cp852" => Encoding::CP852,
62
+ "cp866" => Encoding::IBM866,
63
+ "cp932" => Encoding::Windows_31J,
64
+ "dec8" => nil,
65
+ "eucjpms" => Encoding::EucJP_ms,
66
+ "euckr" => Encoding::EUC_KR,
67
+ "gb2312" => Encoding::EUC_CN,
68
+ "gbk" => Encoding::GBK,
69
+ "geostd8" => nil,
70
+ "greek" => Encoding::ISO_8859_7,
71
+ "hebrew" => Encoding::ISO_8859_8,
72
+ "hp8" => nil,
73
+ "keybcs2" => nil,
74
+ "koi8r" => Encoding::KOI8_R,
75
+ "koi8u" => Encoding::KOI8_U,
76
+ "latin1" => Encoding::ISO_8859_1,
77
+ "latin2" => Encoding::ISO_8859_2,
78
+ "latin5" => Encoding::ISO_8859_9,
79
+ "latin7" => Encoding::ISO_8859_13,
80
+ "macce" => Encoding::MacCentEuro,
81
+ "macroman" => Encoding::MacRoman,
82
+ "sjis" => Encoding::SHIFT_JIS,
83
+ "swe7" => nil,
84
+ "tis620" => Encoding::TIS_620,
85
+ "ucs2" => Encoding::UTF_16BE,
86
+ "ujis" => Encoding::EucJP_ms,
87
+ "utf8" => Encoding::UTF_8,
88
+ }
89
+
90
+ MYSQL_CHARSET_MAP = {
91
+ 1 => {:name => "big5", :collation => "big5_chinese_ci"},
92
+ 2 => {:name => "latin2", :collation => "latin2_czech_cs"},
93
+ 3 => {:name => "dec8", :collation => "dec8_swedish_ci"},
94
+ 4 => {:name => "cp850", :collation => "cp850_general_ci"},
95
+ 5 => {:name => "latin1", :collation => "latin1_german1_ci"},
96
+ 6 => {:name => "hp8", :collation => "hp8_english_ci"},
97
+ 7 => {:name => "koi8r", :collation => "koi8r_general_ci"},
98
+ 8 => {:name => "latin1", :collation => "latin1_swedish_ci"},
99
+ 9 => {:name => "latin2", :collation => "latin2_general_ci"},
100
+ 10 => {:name => "swe7", :collation => "swe7_swedish_ci"},
101
+ 11 => {:name => "ascii", :collation => "ascii_general_ci"},
102
+ 12 => {:name => "ujis", :collation => "ujis_japanese_ci"},
103
+ 13 => {:name => "sjis", :collation => "sjis_japanese_ci"},
104
+ 14 => {:name => "cp1251", :collation => "cp1251_bulgarian_ci"},
105
+ 15 => {:name => "latin1", :collation => "latin1_danish_ci"},
106
+ 16 => {:name => "hebrew", :collation => "hebrew_general_ci"},
107
+ 17 => {:name => "filename", :collation => "filename"},
108
+ 18 => {:name => "tis620", :collation => "tis620_thai_ci"},
109
+ 19 => {:name => "euckr", :collation => "euckr_korean_ci"},
110
+ 20 => {:name => "latin7", :collation => "latin7_estonian_cs"},
111
+ 21 => {:name => "latin2", :collation => "latin2_hungarian_ci"},
112
+ 22 => {:name => "koi8u", :collation => "koi8u_general_ci"},
113
+ 23 => {:name => "cp1251", :collation => "cp1251_ukrainian_ci"},
114
+ 24 => {:name => "gb2312", :collation => "gb2312_chinese_ci"},
115
+ 25 => {:name => "greek", :collation => "greek_general_ci"},
116
+ 26 => {:name => "cp1250", :collation => "cp1250_general_ci"},
117
+ 27 => {:name => "latin2", :collation => "latin2_croatian_ci"},
118
+ 28 => {:name => "gbk", :collation => "gbk_chinese_ci"},
119
+ 29 => {:name => "cp1257", :collation => "cp1257_lithuanian_ci"},
120
+ 30 => {:name => "latin5", :collation => "latin5_turkish_ci"},
121
+ 31 => {:name => "latin1", :collation => "latin1_german2_ci"},
122
+ 32 => {:name => "armscii8", :collation => "armscii8_general_ci"},
123
+ 33 => {:name => "utf8", :collation => "utf8_general_ci"},
124
+ 34 => {:name => "cp1250", :collation => "cp1250_czech_cs"},
125
+ 35 => {:name => "ucs2", :collation => "ucs2_general_ci"},
126
+ 36 => {:name => "cp866", :collation => "cp866_general_ci"},
127
+ 37 => {:name => "keybcs2", :collation => "keybcs2_general_ci"},
128
+ 38 => {:name => "macce", :collation => "macce_general_ci"},
129
+ 39 => {:name => "macroman", :collation => "macroman_general_ci"},
130
+ 40 => {:name => "cp852", :collation => "cp852_general_ci"},
131
+ 41 => {:name => "latin7", :collation => "latin7_general_ci"},
132
+ 42 => {:name => "latin7", :collation => "latin7_general_cs"},
133
+ 43 => {:name => "macce", :collation => "macce_bin"},
134
+ 44 => {:name => "cp1250", :collation => "cp1250_croatian_ci"},
135
+ 47 => {:name => "latin1", :collation => "latin1_bin"},
136
+ 48 => {:name => "latin1", :collation => "latin1_general_ci"},
137
+ 49 => {:name => "latin1", :collation => "latin1_general_cs"},
138
+ 50 => {:name => "cp1251", :collation => "cp1251_bin"},
139
+ 51 => {:name => "cp1251", :collation => "cp1251_general_ci"},
140
+ 52 => {:name => "cp1251", :collation => "cp1251_general_cs"},
141
+ 53 => {:name => "macroman", :collation => "macroman_bin"},
142
+ 57 => {:name => "cp1256", :collation => "cp1256_general_ci"},
143
+ 58 => {:name => "cp1257", :collation => "cp1257_bin"},
144
+ 59 => {:name => "cp1257", :collation => "cp1257_general_ci"},
145
+ 63 => {:name => "binary", :collation => "binary"},
146
+ 64 => {:name => "armscii8", :collation => "armscii8_bin"},
147
+ 65 => {:name => "ascii", :collation => "ascii_bin"},
148
+ 66 => {:name => "cp1250", :collation => "cp1250_bin"},
149
+ 67 => {:name => "cp1256", :collation => "cp1256_bin"},
150
+ 68 => {:name => "cp866", :collation => "cp866_bin"},
151
+ 69 => {:name => "dec8", :collation => "dec8_bin"},
152
+ 70 => {:name => "greek", :collation => "greek_bin"},
153
+ 71 => {:name => "hebrew", :collation => "hebrew_bin"},
154
+ 72 => {:name => "hp8", :collation => "hp8_bin"},
155
+ 73 => {:name => "keybcs2", :collation => "keybcs2_bin"},
156
+ 74 => {:name => "koi8r", :collation => "koi8r_bin"},
157
+ 75 => {:name => "koi8u", :collation => "koi8u_bin"},
158
+ 77 => {:name => "latin2", :collation => "latin2_bin"},
159
+ 78 => {:name => "latin5", :collation => "latin5_bin"},
160
+ 79 => {:name => "latin7", :collation => "latin7_bin"},
161
+ 80 => {:name => "cp850", :collation => "cp850_bin"},
162
+ 81 => {:name => "cp852", :collation => "cp852_bin"},
163
+ 82 => {:name => "swe7", :collation => "swe7_bin"},
164
+ 83 => {:name => "utf8", :collation => "utf8_bin"},
165
+ 84 => {:name => "big5", :collation => "big5_bin"},
166
+ 85 => {:name => "euckr", :collation => "euckr_bin"},
167
+ 86 => {:name => "gb2312", :collation => "gb2312_bin"},
168
+ 87 => {:name => "gbk", :collation => "gbk_bin"},
169
+ 88 => {:name => "sjis", :collation => "sjis_bin"},
170
+ 89 => {:name => "tis620", :collation => "tis620_bin"},
171
+ 90 => {:name => "ucs2", :collation => "ucs2_bin"},
172
+ 91 => {:name => "ujis", :collation => "ujis_bin"},
173
+ 92 => {:name => "geostd8", :collation => "geostd8_general_ci"},
174
+ 93 => {:name => "geostd8", :collation => "geostd8_bin"},
175
+ 94 => {:name => "latin1", :collation => "latin1_spanish_ci"},
176
+ 95 => {:name => "cp932", :collation => "cp932_japanese_ci"},
177
+ 96 => {:name => "cp932", :collation => "cp932_bin"},
178
+ 97 => {:name => "eucjpms", :collation => "eucjpms_japanese_ci"},
179
+ 98 => {:name => "eucjpms", :collation => "eucjpms_bin"},
180
+ 99 => {:name => "cp1250", :collation => "cp1250_polish_ci"},
181
+ 128 => {:name => "ucs2", :collation => "ucs2_unicode_ci"},
182
+ 129 => {:name => "ucs2", :collation => "ucs2_icelandic_ci"},
183
+ 130 => {:name => "ucs2", :collation => "ucs2_latvian_ci"},
184
+ 131 => {:name => "ucs2", :collation => "ucs2_romanian_ci"},
185
+ 132 => {:name => "ucs2", :collation => "ucs2_slovenian_ci"},
186
+ 133 => {:name => "ucs2", :collation => "ucs2_polish_ci"},
187
+ 134 => {:name => "ucs2", :collation => "ucs2_estonian_ci"},
188
+ 135 => {:name => "ucs2", :collation => "ucs2_spanish_ci"},
189
+ 136 => {:name => "ucs2", :collation => "ucs2_swedish_ci"},
190
+ 137 => {:name => "ucs2", :collation => "ucs2_turkish_ci"},
191
+ 138 => {:name => "ucs2", :collation => "ucs2_czech_ci"},
192
+ 139 => {:name => "ucs2", :collation => "ucs2_danish_ci"},
193
+ 140 => {:name => "ucs2", :collation => "ucs2_lithuanian_ci"},
194
+ 141 => {:name => "ucs2", :collation => "ucs2_slovak_ci"},
195
+ 142 => {:name => "ucs2", :collation => "ucs2_spanish2_ci"},
196
+ 143 => {:name => "ucs2", :collation => "ucs2_roman_ci"},
197
+ 144 => {:name => "ucs2", :collation => "ucs2_persian_ci"},
198
+ 145 => {:name => "ucs2", :collation => "ucs2_esperanto_ci"},
199
+ 146 => {:name => "ucs2", :collation => "ucs2_hungarian_ci"},
200
+ 192 => {:name => "utf8", :collation => "utf8_unicode_ci"},
201
+ 193 => {:name => "utf8", :collation => "utf8_icelandic_ci"},
202
+ 194 => {:name => "utf8", :collation => "utf8_latvian_ci"},
203
+ 195 => {:name => "utf8", :collation => "utf8_romanian_ci"},
204
+ 196 => {:name => "utf8", :collation => "utf8_slovenian_ci"},
205
+ 197 => {:name => "utf8", :collation => "utf8_polish_ci"},
206
+ 198 => {:name => "utf8", :collation => "utf8_estonian_ci"},
207
+ 199 => {:name => "utf8", :collation => "utf8_spanish_ci"},
208
+ 200 => {:name => "utf8", :collation => "utf8_swedish_ci"},
209
+ 201 => {:name => "utf8", :collation => "utf8_turkish_ci"},
210
+ 202 => {:name => "utf8", :collation => "utf8_czech_ci"},
211
+ 203 => {:name => "utf8", :collation => "utf8_danish_ci"},
212
+ 204 => {:name => "utf8", :collation => "utf8_lithuanian_ci"},
213
+ 205 => {:name => "utf8", :collation => "utf8_slovak_ci"},
214
+ 206 => {:name => "utf8", :collation => "utf8_spanish2_ci"},
215
+ 207 => {:name => "utf8", :collation => "utf8_roman_ci"},
216
+ 208 => {:name => "utf8", :collation => "utf8_persian_ci"},
217
+ 209 => {:name => "utf8", :collation => "utf8_esperanto_ci"},
218
+ 210 => {:name => "utf8", :collation => "utf8_hungarian_ci"},
219
+ 254 => {:name => "utf8", :collation => "utf8_general_cs"}
220
+ }
221
+
222
+ def self.encoding_from_charset(charset)
223
+ CHARSET_MAP[charset.to_s.downcase]
224
+ end
225
+
226
+ def self.encoding_from_charset_code(code)
227
+ if mapping = MYSQL_CHARSET_MAP[code]
228
+ encoding_from_charset(mapping[:name])
229
+ else
230
+ nil
231
+ end
232
+ end
233
+ end
234
+
235
+ private
236
+ def self.local_offset
237
+ ::Time.local(2010).utc_offset.to_r / 86400
238
+ end
239
+ end
240
+ end
@@ -0,0 +1,37 @@
1
+ # encoding: utf-8
2
+
3
+ require 'eventmachine'
4
+ require 'mysql2'
5
+
6
+ module Mysql2
7
+ module EM
8
+ class Client < ::Mysql2::Client
9
+ module Watcher
10
+ def initialize(client, deferable)
11
+ @client = client
12
+ @deferable = deferable
13
+ end
14
+
15
+ def notify_readable
16
+ detach
17
+ begin
18
+ @deferable.succeed(@client.async_result)
19
+ rescue Exception => e
20
+ @deferable.fail(e)
21
+ end
22
+ end
23
+ end
24
+
25
+ def query(sql, opts={})
26
+ if ::EM.reactor_running?
27
+ super(sql, opts.merge(:async => true))
28
+ deferable = ::EM::DefaultDeferrable.new
29
+ ::EM.watch(self.socket, Watcher, self, deferable).notify_readable = true
30
+ deferable
31
+ else
32
+ super(sql, opts)
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,31 @@
1
+ # encoding: utf-8
2
+
3
+ require 'mysql2/em'
4
+ require 'fiber'
5
+
6
+ module Mysql2
7
+ module EM
8
+ module Fiber
9
+ class Client < ::Mysql2::EM::Client
10
+ def query(sql, opts={})
11
+ if ::EM.reactor_running?
12
+ deferable = super(sql, opts)
13
+
14
+ fiber = ::Fiber.current
15
+ deferable.callback do |result|
16
+ fiber.resume(result)
17
+ end
18
+ deferable.errback do |err|
19
+ fiber.resume(err)
20
+ end
21
+ ::Fiber.yield.tap do |result|
22
+ raise result if result.is_a?(::Exception)
23
+ end
24
+ else
25
+ super(sql, opts)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,15 @@
1
+ module Mysql2
2
+ class Error < StandardError
3
+ attr_accessor :error_number, :sql_state
4
+
5
+ def initialize msg
6
+ super
7
+ @error_number = nil
8
+ @sql_state = nil
9
+ end
10
+
11
+ # Mysql gem compatibility
12
+ alias_method :errno, :error_number
13
+ alias_method :error, :message
14
+ end
15
+ end