phonelib 0.2.4 → 0.2.5
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +11 -6
- data/Rakefile +1 -1
- data/data/PhoneNumberMetaData.xml +39 -45
- data/data/phone_data.yml +6482 -6470
- data/lib/phonelib.rb +2 -1
- data/lib/phonelib/core.rb +30 -29
- data/lib/phonelib/phone.rb +52 -32
- data/lib/phonelib/version.rb +2 -1
- data/lib/tasks/phonelib_tasks.rake +14 -9
- data/lib/validators/phone_validator.rb +1 -1
- data/test/dummy/Rakefile +2 -1
- data/test/dummy/db/development.sqlite3 +0 -0
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/dummy/log/development.log +16 -28
- data/test/dummy/log/test.log +1351 -4867
- data/test/phonelib_test.rb +35 -4
- metadata +122 -126
- checksums.yaml +0 -7
data/lib/phonelib.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# main module definition
|
1
2
|
module Phonelib
|
2
3
|
# load gem classes
|
3
4
|
autoload :Core, 'phonelib/core'
|
@@ -8,6 +9,6 @@ module Phonelib
|
|
8
9
|
}
|
9
10
|
end
|
10
11
|
|
11
|
-
if defined?(
|
12
|
+
if defined?(ActiveModel)
|
12
13
|
autoload :PhoneValidator, 'validators/phone_validator'
|
13
14
|
end
|
data/lib/phonelib/core.rb
CHANGED
@@ -16,25 +16,25 @@ module Phonelib
|
|
16
16
|
def default_country=(country)
|
17
17
|
@@default_country = country
|
18
18
|
end
|
19
|
-
|
19
|
+
|
20
20
|
# gem constants definition
|
21
21
|
# constants for phone types
|
22
22
|
|
23
23
|
# Validation patterns keys constants
|
24
24
|
# General pattern for country key
|
25
|
-
GENERAL = :
|
25
|
+
GENERAL = :general_desc
|
26
26
|
# Freephone line pattern key
|
27
|
-
PREMIUM_RATE = :
|
27
|
+
PREMIUM_RATE = :premium_rate
|
28
28
|
# Freephone line pattern key
|
29
|
-
TOLL_FREE = :
|
29
|
+
TOLL_FREE = :toll_free
|
30
30
|
# Shared cost pattern key. The cost of this call is shared between caller
|
31
31
|
# and recipient, and is hence typically less than PREMIUM_RATE calls
|
32
|
-
SHARED_COST = :
|
33
|
-
#
|
32
|
+
SHARED_COST = :shared_cost
|
33
|
+
# VoIP pattern key. This includes TSoIP (Telephony Service over IP)
|
34
34
|
VOIP = :voip
|
35
35
|
# A personal number is associated with a particular person, and may be
|
36
36
|
# routed to either a MOBILE or FIXED_LINE number.
|
37
|
-
PERSONAL_NUMBER = :
|
37
|
+
PERSONAL_NUMBER = :personal_number
|
38
38
|
# Pager phone number pattern key
|
39
39
|
PAGER = :pager
|
40
40
|
# Used for 'Universal Access Numbers' or 'Company Numbers'. They may be
|
@@ -44,48 +44,48 @@ module Phonelib
|
|
44
44
|
# Used for 'Voice Mail Access Numbers'.
|
45
45
|
VOICEMAIL = :voicemail
|
46
46
|
# Fixed line pattern key
|
47
|
-
FIXED_LINE = :
|
47
|
+
FIXED_LINE = :fixed_line
|
48
48
|
# Mobile phone number pattern key
|
49
49
|
MOBILE = :mobile
|
50
50
|
# In case MOBILE and FIXED patterns are the same, this type is returned
|
51
|
-
FIXED_OR_MOBILE = :
|
51
|
+
FIXED_OR_MOBILE = :fixed_or_mobile
|
52
52
|
|
53
53
|
# Internal use keys for validations
|
54
54
|
# Valid regex pattern key
|
55
|
-
VALID_PATTERN = :
|
55
|
+
VALID_PATTERN = :national_number_pattern
|
56
56
|
# Possible regex pattern key
|
57
|
-
POSSIBLE_PATTERN = :
|
57
|
+
POSSIBLE_PATTERN = :possible_number_pattern
|
58
58
|
# National prefix key
|
59
|
-
NATIONAL_PREFIX = :
|
59
|
+
NATIONAL_PREFIX = :national_prefix
|
60
60
|
# National prefix rule key
|
61
|
-
NATIONAL_PREFIX_RULE = :
|
61
|
+
NATIONAL_PREFIX_RULE = :national_prefix_formatting_rule
|
62
62
|
# Country code key
|
63
|
-
COUNTRY_CODE = :
|
63
|
+
COUNTRY_CODE = :country_code
|
64
64
|
|
65
65
|
# Default number formatting data hash
|
66
66
|
DEFAULT_NUMBER_FORMAT = {
|
67
|
-
pattern:
|
68
|
-
format:
|
67
|
+
pattern: '(\\d+)(\\d{3})(\\d{4})',
|
68
|
+
format: '$1 $2 $3'
|
69
69
|
}
|
70
70
|
|
71
71
|
# hash of all phone types with human representation
|
72
72
|
TYPES = {
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
73
|
+
general_desc: 'General Pattern',
|
74
|
+
premium_rate: 'Premium Rate',
|
75
|
+
toll_free: 'Toll Free',
|
76
|
+
shared_cost: 'Shared Cost',
|
77
77
|
voip: 'VoIP',
|
78
|
-
|
78
|
+
personal_number: 'Personal Number',
|
79
79
|
pager: 'Pager',
|
80
80
|
uan: 'UAN',
|
81
81
|
voicemail: 'VoiceMail',
|
82
|
-
|
82
|
+
fixed_line: 'Fixed Line',
|
83
83
|
mobile: 'Mobile',
|
84
|
-
|
84
|
+
fixed_or_mobile: 'Fixed Line or Mobile'
|
85
85
|
}
|
86
86
|
|
87
87
|
# array of types not included for validation check in cycle
|
88
|
-
NOT_FOR_CHECK = [
|
88
|
+
NOT_FOR_CHECK = [:general_desc, :fixed_line, :mobile, :fixed_or_mobile]
|
89
89
|
|
90
90
|
# method for parsing phone number.
|
91
91
|
# On first run fills @@phone_data with data present in yaml file
|
@@ -137,6 +137,7 @@ module Phonelib
|
|
137
137
|
end
|
138
138
|
|
139
139
|
private
|
140
|
+
|
140
141
|
# Load data file into memory
|
141
142
|
def load_data
|
142
143
|
require 'yaml'
|
@@ -152,18 +153,18 @@ module Phonelib
|
|
152
153
|
|
153
154
|
# Get Phone instance for provided phone with country specified
|
154
155
|
def detect_and_parse_by_country(phone, country)
|
155
|
-
detected = @@phone_data.
|
156
|
-
if
|
156
|
+
detected = @@phone_data.find { |data| data[:id] == country }
|
157
|
+
if detected
|
157
158
|
phone = convert_phone_to_e164(phone,
|
158
|
-
detected[:
|
159
|
-
detected[:
|
159
|
+
detected[:country_code],
|
160
|
+
detected[:national_prefix])
|
160
161
|
end
|
161
162
|
Phonelib::Phone.new(phone, [detected])
|
162
163
|
end
|
163
164
|
|
164
165
|
# Create phone representation in e164 format
|
165
166
|
def convert_phone_to_e164(phone, prefix, national_prefix)
|
166
|
-
return phone if phone.gsub('+','').start_with?(prefix)
|
167
|
+
return phone if phone.gsub('+', '').start_with?(prefix)
|
167
168
|
if !!national_prefix && phone.start_with?(national_prefix)
|
168
169
|
phone = phone[1..phone.length]
|
169
170
|
end
|
data/lib/phonelib/phone.rb
CHANGED
@@ -21,7 +21,7 @@ module Phonelib
|
|
21
21
|
|
22
22
|
# Returns all phone types that matched valid patterns
|
23
23
|
def types
|
24
|
-
@analyzed_data.flat_map {|iso2, data| data[:valid]}.uniq
|
24
|
+
@analyzed_data.flat_map { |iso2, data| data[:valid] }.uniq
|
25
25
|
end
|
26
26
|
|
27
27
|
# Returns first phone type that matched
|
@@ -29,21 +29,40 @@ module Phonelib
|
|
29
29
|
types.first
|
30
30
|
end
|
31
31
|
|
32
|
+
# Returns human representation of all matched phone types
|
33
|
+
def human_types
|
34
|
+
types.map { |type| Core::TYPES[type] }
|
35
|
+
end
|
36
|
+
|
37
|
+
# Return human representation of phone type
|
38
|
+
def human_type
|
39
|
+
Core::TYPES[type]
|
40
|
+
end
|
41
|
+
|
32
42
|
# Returns all countries that matched valid patterns
|
33
43
|
def countries
|
34
|
-
@analyzed_data.map {|iso2, data| iso2}
|
44
|
+
@analyzed_data.map { |iso2, data| iso2 }
|
45
|
+
end
|
46
|
+
|
47
|
+
# Return countries with valid patterns
|
48
|
+
def valid_countries
|
49
|
+
@valid_countries ||= countries.select do |iso2|
|
50
|
+
@analyzed_data[iso2][:valid].any?
|
51
|
+
end
|
35
52
|
end
|
36
53
|
|
37
54
|
# Returns first country that matched valid patterns
|
38
55
|
def country
|
39
|
-
@country ||=
|
40
|
-
|
56
|
+
@country ||= begin
|
57
|
+
valid_countries.find do |iso2|
|
58
|
+
@analyzed_data[iso2][:main_country_for_code] == 'true'
|
59
|
+
end || valid_countries.first
|
41
60
|
end
|
42
61
|
end
|
43
62
|
|
44
63
|
# Returns whether a current parsed phone number is valid
|
45
64
|
def valid?
|
46
|
-
@analyzed_data.select {|iso2, data| data[:valid].any? }.any?
|
65
|
+
@analyzed_data.select { |iso2, data| data[:valid].any? }.any?
|
47
66
|
end
|
48
67
|
|
49
68
|
# Returns whether a current parsed phone number is invalid
|
@@ -53,7 +72,7 @@ module Phonelib
|
|
53
72
|
|
54
73
|
# Returns whether a current parsed phone number is possible
|
55
74
|
def possible?
|
56
|
-
@analyzed_data.select {|iso2, data| data[:possible].any? }.any?
|
75
|
+
@analyzed_data.select { |iso2, data| data[:possible].any? }.any?
|
57
76
|
end
|
58
77
|
|
59
78
|
# Returns whether a current parsed phone number is impossible
|
@@ -68,11 +87,11 @@ module Phonelib
|
|
68
87
|
|
69
88
|
# add space to format groups, change first group to rule,
|
70
89
|
# change rule's constants to values
|
71
|
-
format_string = format[:format].gsub(/(\d)\$/,
|
72
|
-
gsub(/(\$NP|\$FG)/,
|
90
|
+
format_string = format[:format].gsub(/(\d)\$/, '\\1 $').gsub('$1', rule)
|
91
|
+
.gsub(/(\$NP|\$FG)/, '$NP' => prefix, '$FG' => '$1')
|
73
92
|
|
74
93
|
matches = @national_number.match(/#{format[:pattern]}/)
|
75
|
-
format_string.gsub(/\$\d/) {|el| matches[el[1].to_i] }
|
94
|
+
format_string.gsub(/\$\d/) { |el| matches[el[1].to_i] }
|
76
95
|
end
|
77
96
|
|
78
97
|
# Returns e164 formatted phone number
|
@@ -93,12 +112,14 @@ module Phonelib
|
|
93
112
|
#
|
94
113
|
# ==== Attributes
|
95
114
|
#
|
96
|
-
# * +country+ - ISO code of country (2 letters) like 'US', 'us' or :us
|
115
|
+
# * +country+ - ISO code of country (2 letters) like 'US', 'us' or :us
|
116
|
+
# for United States
|
97
117
|
#
|
98
118
|
def valid_for_country?(country)
|
99
119
|
country = country.to_s.upcase
|
100
|
-
@analyzed_data.select
|
101
|
-
|
120
|
+
@analyzed_data.select do |iso2, data|
|
121
|
+
country == iso2 && data[:valid].any?
|
122
|
+
end.any?
|
102
123
|
end
|
103
124
|
|
104
125
|
# Returns whether a current parsed phone number is invalid for specified
|
@@ -106,13 +127,15 @@ module Phonelib
|
|
106
127
|
#
|
107
128
|
# ==== Attributes
|
108
129
|
#
|
109
|
-
# * +country+ - ISO code of country (2 letters) like 'US', 'us' or :us
|
130
|
+
# * +country+ - ISO code of country (2 letters) like 'US', 'us' or :us
|
131
|
+
# for United States
|
110
132
|
#
|
111
133
|
def invalid_for_country?(country)
|
112
134
|
!valid_for_country?(country)
|
113
135
|
end
|
114
136
|
|
115
137
|
private
|
138
|
+
|
116
139
|
# Get needable data for formatting phone as national number
|
117
140
|
def get_formatting_data
|
118
141
|
format = @analyzed_data[country][:format]
|
@@ -126,13 +149,15 @@ module Phonelib
|
|
126
149
|
# Analyze current phone with provided data hash
|
127
150
|
def analyze_phone(country_data)
|
128
151
|
country_data.each do |data|
|
129
|
-
|
130
|
-
|
131
|
-
|
152
|
+
phone_code = "#{data[:country_code]}#{data[:leading_digits]}"
|
153
|
+
if @sanitized.start_with? phone_code
|
154
|
+
_possible, valid = get_patterns(data[:types][Core::GENERAL])
|
155
|
+
next unless /^#{data[:country_code]}#{valid}$/ =~ @sanitized
|
132
156
|
|
133
|
-
@national_number = @sanitized[data[:
|
157
|
+
@national_number = @sanitized[data[:country_code].length..-1]
|
134
158
|
@analyzed_data[data[:id]] = data
|
135
|
-
@analyzed_data[data[:id]][:format] =
|
159
|
+
@analyzed_data[data[:id]][:format] =
|
160
|
+
get_number_format(data[:formats])
|
136
161
|
@analyzed_data[data[:id]].merge! all_number_types(data[:types])
|
137
162
|
end
|
138
163
|
end
|
@@ -141,16 +166,11 @@ module Phonelib
|
|
141
166
|
# Returns all valid and possible phone number types for currently parsed
|
142
167
|
# phone for provided data hash.
|
143
168
|
def all_number_types(data)
|
144
|
-
response = {valid: [], possible: []}
|
145
|
-
|
146
|
-
|
147
|
-
(Core::TYPES.keys - Core::NOT_FOR_CHECK +
|
148
|
-
check_type =
|
149
|
-
when Core::FIXED_OR_MOBILE
|
150
|
-
Core::FIXED_LINE
|
151
|
-
else
|
152
|
-
type
|
153
|
-
end
|
169
|
+
response = { valid: [], possible: [] }
|
170
|
+
|
171
|
+
additional = check_same_types(data)
|
172
|
+
(Core::TYPES.keys - Core::NOT_FOR_CHECK + additional).each do |type|
|
173
|
+
check_type = (type == Core::FIXED_OR_MOBILE ? Core::FIXED_LINE : type)
|
154
174
|
possible, valid = get_patterns(data[check_type])
|
155
175
|
|
156
176
|
response[:possible] << type if number_possible?(possible)
|
@@ -164,8 +184,8 @@ module Phonelib
|
|
164
184
|
def get_number_format(format_data)
|
165
185
|
if format_data
|
166
186
|
format_data.find do |format|
|
167
|
-
(format[:
|
168
|
-
|| /^#{format[:
|
187
|
+
(format[:leading_digits].nil? \
|
188
|
+
|| /^#{format[:leading_digits]}/ =~ @national_number) \
|
169
189
|
&& /^#{format[:pattern]}$/ =~ @national_number
|
170
190
|
end
|
171
191
|
else
|
@@ -176,9 +196,9 @@ module Phonelib
|
|
176
196
|
# Checks if fixed line pattern and mobile pattern are the same
|
177
197
|
def check_same_types(data)
|
178
198
|
if data[Core::FIXED_LINE] == data[Core::MOBILE]
|
179
|
-
[
|
199
|
+
[Core::FIXED_OR_MOBILE]
|
180
200
|
else
|
181
|
-
[
|
201
|
+
[Core::FIXED_LINE, Core::MOBILE]
|
182
202
|
end
|
183
203
|
end
|
184
204
|
|
data/lib/phonelib/version.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
namespace :phonelib do
|
2
2
|
|
3
|
-
desc
|
3
|
+
desc 'Import and reparse original data file from Google libphonenumber'
|
4
4
|
task :import_data do
|
5
5
|
require 'net/http'
|
6
6
|
require 'yaml'
|
@@ -11,7 +11,7 @@ namespace :phonelib do
|
|
11
11
|
xml_data = Net::HTTP.get_response(URI.parse(url)).body
|
12
12
|
|
13
13
|
# save in file for debug
|
14
|
-
File.open(
|
14
|
+
File.open('data/PhoneNumberMetaData.xml', 'w+') do |f|
|
15
15
|
f.write(xml_data)
|
16
16
|
end
|
17
17
|
|
@@ -24,20 +24,20 @@ namespace :phonelib do
|
|
24
24
|
# each country
|
25
25
|
country = {}
|
26
26
|
el.attributes.each do |k, v|
|
27
|
-
country[k.to_sym] = v.to_s.tr(" \n",
|
27
|
+
country[camel2snake(k).to_sym] = v.to_s.tr(" \n", '')
|
28
28
|
end
|
29
29
|
|
30
30
|
country[:types] = {}
|
31
31
|
|
32
32
|
el.children.each do | phone_type |
|
33
33
|
if !%w(comment text).include?(phone_type.name)
|
34
|
-
phone_type_sym = phone_type.name.to_sym
|
34
|
+
phone_type_sym = camel2snake(phone_type.name).to_sym
|
35
35
|
|
36
36
|
if phone_type.name != 'availableFormats'
|
37
37
|
country[:types][phone_type_sym] = {}
|
38
38
|
phone_type.elements.each do |pattern|
|
39
|
-
country[:types][phone_type_sym][pattern.name.to_sym] =
|
40
|
-
pattern.children.first.to_s.tr(" \n",
|
39
|
+
country[:types][phone_type_sym][camel2snake(pattern.name).to_sym] =
|
40
|
+
pattern.children.first.to_s.tr(" \n", '')
|
41
41
|
end
|
42
42
|
else
|
43
43
|
country[:formats] = []
|
@@ -46,12 +46,13 @@ namespace :phonelib do
|
|
46
46
|
if !%w(comment text).include?(format.name)
|
47
47
|
current_format = {}
|
48
48
|
format.each do |f|
|
49
|
-
current_format[f[0].to_sym] = f[1]
|
49
|
+
current_format[camel2snake(f[0]).to_sym] = f[1]
|
50
50
|
end
|
51
51
|
|
52
52
|
format.children.each do |f|
|
53
53
|
if f.name != 'text'
|
54
|
-
current_format[f.name.to_sym] =
|
54
|
+
current_format[camel2snake(f.name).to_sym] =
|
55
|
+
f.children.first.to_s.gsub(/\n\s+/, '')
|
55
56
|
end
|
56
57
|
end
|
57
58
|
|
@@ -66,8 +67,12 @@ namespace :phonelib do
|
|
66
67
|
countries.push(country)
|
67
68
|
end
|
68
69
|
target = 'data/phone_data.yml'
|
69
|
-
File.open(target,
|
70
|
+
File.open(target, 'w+') do |f|
|
70
71
|
f.write(countries.to_yaml)
|
71
72
|
end
|
72
73
|
end
|
74
|
+
|
75
|
+
def camel2snake(s)
|
76
|
+
s.gsub(/[A-Z]+/) { |m| "_#{m.downcase}" }
|
77
|
+
end
|
73
78
|
end
|
data/test/dummy/Rakefile
CHANGED
Binary file
|
data/test/dummy/db/test.sqlite3
CHANGED
Binary file
|
@@ -1,31 +1,19 @@
|
|
1
1
|
Connecting to database specified by database.yml
|
2
|
-
[1m[36m (0.
|
3
|
-
[1m[35m (
|
4
|
-
[1m[36m (
|
5
|
-
[1m[35m (
|
6
|
-
|
7
|
-
[1m[
|
8
|
-
[1m[
|
9
|
-
[1m[
|
10
|
-
[1m[35m (4.1ms)[0m commit transaction
|
11
|
-
Migrating to AddPossibleNumberToPhone (20130122075331)
|
12
|
-
[1m[36m (0.1ms)[0m [1mbegin transaction[0m
|
13
|
-
[1m[35m (0.6ms)[0m ALTER TABLE "phones" ADD "possible_number" varchar(255)
|
14
|
-
[1m[36m (0.1ms)[0m [1mINSERT INTO "schema_migrations" ("version") VALUES ('20130122075331')[0m
|
15
|
-
[1m[35m (2.7ms)[0m commit transaction
|
16
|
-
[1m[36m (0.2ms)[0m [1mSELECT "schema_migrations"."version" FROM "schema_migrations" [0m
|
2
|
+
[1m[36m (0.9ms)[0m [1mselect sqlite_version(*)[0m
|
3
|
+
[1m[35m (6.8ms)[0m CREATE TABLE "phones" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "number" varchar(255), "created_at" datetime NOT NULL, "updated_at" datetime NOT NULL, "possible_number" varchar(255))
|
4
|
+
[1m[36m (1.4ms)[0m [1mCREATE TABLE "schema_migrations" ("version" varchar(255) NOT NULL) [0m
|
5
|
+
[1m[35m (1.6ms)[0m CREATE UNIQUE INDEX "unique_schema_migrations" ON "schema_migrations" ("version")
|
6
|
+
[1m[36m (0.1ms)[0m [1mSELECT version FROM "schema_migrations"[0m
|
7
|
+
[1m[35m (3.2ms)[0m INSERT INTO "schema_migrations" (version) VALUES ('20130122075331')
|
8
|
+
[1m[36m (1.3ms)[0m [1mINSERT INTO "schema_migrations" (version) VALUES ('20130121173847')[0m
|
9
|
+
[1m[35m (0.7ms)[0m SELECT "schema_migrations"."version" FROM "schema_migrations"
|
17
10
|
Connecting to database specified by database.yml
|
18
|
-
[1m[36m (0.
|
19
|
-
[1m[35m (
|
20
|
-
[1m[36m (3.
|
21
|
-
[1m[35m (
|
22
|
-
[1m[36m (
|
23
|
-
Connecting to database specified by database.yml
|
24
|
-
[1m[36m (0.1ms)[0m [1mSELECT "schema_migrations"."version" FROM "schema_migrations" [0m
|
25
|
-
[1m[35m (0.3ms)[0m select sqlite_version(*)
|
26
|
-
[1m[36m (5.1ms)[0m [1mCREATE TABLE "phones" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "number" varchar(255), "created_at" datetime NOT NULL, "updated_at" datetime NOT NULL, "possible_number" varchar(255)) [0m
|
27
|
-
[1m[35m (2.3ms)[0m CREATE TABLE "schema_migrations" ("version" varchar(255) NOT NULL)
|
28
|
-
[1m[36m (3.4ms)[0m [1mCREATE UNIQUE INDEX "unique_schema_migrations" ON "schema_migrations" ("version")[0m
|
11
|
+
[1m[36m (0.6ms)[0m [1mSELECT "schema_migrations"."version" FROM "schema_migrations" [0m
|
12
|
+
[1m[35m (0.1ms)[0m select sqlite_version(*)
|
13
|
+
[1m[36m (3.7ms)[0m [1mCREATE TABLE "phones" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "number" varchar(255), "created_at" datetime NOT NULL, "updated_at" datetime NOT NULL, "possible_number" varchar(255)) [0m
|
14
|
+
[1m[35m (1.4ms)[0m CREATE TABLE "schema_migrations" ("version" varchar(255) NOT NULL)
|
15
|
+
[1m[36m (1.7ms)[0m [1mCREATE UNIQUE INDEX "unique_schema_migrations" ON "schema_migrations" ("version")[0m
|
29
16
|
[1m[35m (0.1ms)[0m SELECT version FROM "schema_migrations"
|
30
|
-
[1m[36m (
|
31
|
-
[1m[35m (
|
17
|
+
[1m[36m (1.0ms)[0m [1mINSERT INTO "schema_migrations" (version) VALUES ('20130122075331')[0m
|
18
|
+
[1m[35m (4.9ms)[0m INSERT INTO "schema_migrations" (version) VALUES ('20130121173847')
|
19
|
+
Connecting to database specified by database.yml
|