pt-osc 0.2.2 → 0.2.3
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/CHANGELOG.md +4 -0
- data/README.md +5 -4
- data/lib/active_record/connection_adapters/mysql_pt_osc_adapter.rb +14 -0
- data/lib/active_record/pt_osc_migration.rb +5 -0
- data/lib/pt-osc/version.rb +1 -1
- data/test/functional/mysql_pt_osc_adapter_test.rb +76 -0
- data/test/functional/pt_osc_migration_functional_test.rb +3 -3
- data/test/integration/pt_osc_migration_integration_test.rb +4 -22
- data/test/test_helper.rb +27 -0
- data/test/unit/mysql_pt_osc_adapter_test.rb +15 -8
- metadata +4 -4
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
[](https://travis-ci.org/steverice/pt-osc)
|
|
2
|
+
[](https://codeclimate.com/github/steverice/pt-osc)
|
|
3
|
+
[](https://codeclimate.com/github/steverice/pt-osc)
|
|
4
|
+
[](http://badge.fury.io/rb/pt-osc)
|
|
5
|
+
[](https://gemnasium.com/steverice/pt-osc)
|
|
5
6
|
|
|
6
7
|
## `pt-online-schema-change` migrations
|
|
7
8
|
|
|
@@ -123,10 +123,24 @@ module ActiveRecord
|
|
|
123
123
|
|
|
124
124
|
protected
|
|
125
125
|
def add_command(table_name, command)
|
|
126
|
+
warn (<<-WARN
|
|
127
|
+
You are trying to ALTER table "#{table_name}" with the mysql_pt_osc adapter without using an PtOscMigration.
|
|
128
|
+
Be aware that ALTER commands will only be executed via pt-online-schema-change inside of an ActiveRecord::PtOscMigration.
|
|
129
|
+
It is likely that although your migration will complete, no schema alterations have been made.
|
|
130
|
+
WARN
|
|
131
|
+
) if caller.any? { |c| c.include? 'active_record/migration.rb' } && caller.none? { |c| c.include? 'active_record/pt_osc_migration.rb' }
|
|
126
132
|
get_commands[table_name] ||= []
|
|
127
133
|
get_commands[table_name] << command
|
|
128
134
|
end
|
|
129
135
|
|
|
136
|
+
# Provides the opportunity to handle warnings in a custom way
|
|
137
|
+
# @param [String] message
|
|
138
|
+
def warn(message)
|
|
139
|
+
# Rake tasks loaded through Railties had warnings silenced before Rails 4.1
|
|
140
|
+
# @see https://github.com/rails/rails/pull/11601
|
|
141
|
+
defined?(Rails) && Rails.version < '4.1' ? enable_warnings { super } : super
|
|
142
|
+
end
|
|
143
|
+
|
|
130
144
|
def get_commands(table_name = nil)
|
|
131
145
|
@osc_commands ||= {}
|
|
132
146
|
table_name.nil? ? @osc_commands : @osc_commands[table_name]
|
|
@@ -88,6 +88,11 @@ module ActiveRecord
|
|
|
88
88
|
end
|
|
89
89
|
end
|
|
90
90
|
|
|
91
|
+
def method_missing(method, *arguments, &block) # :nodoc:
|
|
92
|
+
# Putting this here ensures that pt_osc_migration shows up in the caller trace
|
|
93
|
+
super
|
|
94
|
+
end
|
|
95
|
+
|
|
91
96
|
protected
|
|
92
97
|
def execute_pt_osc
|
|
93
98
|
return unless @connection.is_a? ActiveRecord::ConnectionAdapters::MysqlPtOscAdapter
|
data/lib/pt-osc/version.rb
CHANGED
|
@@ -3,6 +3,8 @@ require 'yaml'
|
|
|
3
3
|
|
|
4
4
|
class MysqlPtOscAdapterTest < Test::Unit::TestCase
|
|
5
5
|
class TestConnection < ActiveRecord::Base; end
|
|
6
|
+
class TestArMigration < ActiveRecord::Migration; end
|
|
7
|
+
class TestPtOscMigration < ActiveRecord::PtOscMigration; end
|
|
6
8
|
|
|
7
9
|
def test_connection
|
|
8
10
|
# Test that we can open a connection with the pt_osc adapter
|
|
@@ -18,4 +20,78 @@ class MysqlPtOscAdapterTest < Test::Unit::TestCase
|
|
|
18
20
|
# Test that the adapter can be loaded when using the activerecord-import gem
|
|
19
21
|
assert_nothing_raised { require 'activerecord-import' }
|
|
20
22
|
end
|
|
23
|
+
|
|
24
|
+
context 'connected using stubbed pt-osc adapter' do
|
|
25
|
+
setup do
|
|
26
|
+
ActiveRecord::Base.establish_connection(test_spec)
|
|
27
|
+
ActiveRecord::ConnectionAdapters::MysqlPtOscAdapter.stubs(:execute)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
teardown do
|
|
31
|
+
ActiveRecord::ConnectionAdapters::MysqlPtOscAdapter.unstub(:execute)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
context 'an ActiveRecord::Migration' do
|
|
35
|
+
setup do
|
|
36
|
+
@migration = TestArMigration.new
|
|
37
|
+
@migration.instance_variable_set(:@connection, ActiveRecord::Base.connection)
|
|
38
|
+
@migration.stubs(:write)
|
|
39
|
+
@migration.stubs(:announce)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
teardown do
|
|
43
|
+
@migration.unstub(:write, :announce)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
should 'raise a warning when adding columns' do
|
|
47
|
+
add_column_fixtures.each do |fixture|
|
|
48
|
+
fixture_command = sprintf(fixture[:command], table: 'a', column: 'b', default: 0, nullable: true)
|
|
49
|
+
@migration.class.class_eval "def change; #{fixture_command}; end"
|
|
50
|
+
ActiveRecord::ConnectionAdapters::MysqlPtOscAdapter.any_instance.expects(:warn).once
|
|
51
|
+
@migration.migrate(:up)
|
|
52
|
+
@migration.class.send(:remove_method, :change)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
should 'explicitly enable warnings for Rails versions before 4.1' do
|
|
57
|
+
fixture = add_column_fixtures.first
|
|
58
|
+
fixture_command = sprintf(fixture[:command], table: 'a', column: 'b', default: 0, nullable: true)
|
|
59
|
+
@migration.class.class_eval "def change; #{fixture_command}; end"
|
|
60
|
+
%w(3.1 3.1.8 3.2 3.2.20 4.0 4.0.12).each do |rails_version|
|
|
61
|
+
Rails.stubs(:version).returns(rails_version)
|
|
62
|
+
Object.any_instance.expects(:enable_warnings).once.yields
|
|
63
|
+
silence_warnings { @migration.migrate(:up) }
|
|
64
|
+
end
|
|
65
|
+
%w(4.1 4.1.9 4.2 4.2.0.beta4 4.2.1 5 5.0).each do |rails_version|
|
|
66
|
+
Rails.stubs(:version).returns(rails_version)
|
|
67
|
+
Object.any_instance.expects(:enable_warnings).never
|
|
68
|
+
silence_warnings { @migration.migrate(:up) }
|
|
69
|
+
end
|
|
70
|
+
@migration.class.send(:remove_method, :change)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
context 'an ActiveRecord::PtOscMigration' do
|
|
75
|
+
setup do
|
|
76
|
+
@migration = TestPtOscMigration.new
|
|
77
|
+
@migration.instance_variable_set(:@connection, ActiveRecord::Base.connection)
|
|
78
|
+
@migration.stubs(:write)
|
|
79
|
+
@migration.stubs(:announce)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
teardown do
|
|
83
|
+
@migration.unstub(:write, :announce)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
should 'not raise a warning when adding columns' do
|
|
87
|
+
add_column_fixtures.each do |fixture|
|
|
88
|
+
fixture_command = sprintf(fixture[:command], table: 'a', column: 'b', default: 0, nullable: true)
|
|
89
|
+
@migration.class.class_eval "def change; #{fixture_command}; end"
|
|
90
|
+
ActiveRecord::ConnectionAdapters::MysqlPtOscAdapter.any_instance.expects(:warn).never
|
|
91
|
+
@migration.migrate(:up)
|
|
92
|
+
@migration.class.send(:remove_method, :change)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
21
97
|
end
|
|
@@ -22,7 +22,7 @@ class PtOscMigrationFunctionalTest < ActiveRecord::TestCase
|
|
|
22
22
|
context 'on an existing table with an existing column' do
|
|
23
23
|
setup do
|
|
24
24
|
@table_name = Faker::Lorem.word
|
|
25
|
-
@column_name =
|
|
25
|
+
@column_name = 'foobar'
|
|
26
26
|
@index_name = Faker::Lorem.words.join('_')
|
|
27
27
|
|
|
28
28
|
ActiveRecord::Base.connection.execute "DROP TABLE IF EXISTS `#{@table_name}`;"
|
|
@@ -40,8 +40,8 @@ class PtOscMigrationFunctionalTest < ActiveRecord::TestCase
|
|
|
40
40
|
|
|
41
41
|
context 'a migration with only ALTER statements' do
|
|
42
42
|
setup do
|
|
43
|
-
@renamed_column_name =
|
|
44
|
-
@new_column_name =
|
|
43
|
+
@renamed_column_name = 'foobar'
|
|
44
|
+
@new_column_name = 'bazqux'
|
|
45
45
|
@new_table_name = Faker::Lorem.word
|
|
46
46
|
@index_name_2 = "#{@index_name}_2"
|
|
47
47
|
@index_name_3 = "#{@index_name}_3"
|
|
@@ -46,32 +46,14 @@ class PtOscMigrationIntegrationTest < ActiveRecord::TestCase
|
|
|
46
46
|
]
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
# https://github.com/rails/rails/blob/fcf9b712b1dbbcb8f48644e6f20676ad9480ba66/activerecord/lib/active_record/type/time.rb#L16
|
|
51
|
-
base_date = Time.utc(2000, 1, 1, 0, 0, 0)
|
|
52
|
-
|
|
53
|
-
datetime_value = Time.at(Time.now.to_i).utc # Date types we're testing don't have sub-second precision
|
|
54
|
-
date_value = Time.utc(datetime_value.year, datetime_value.month, datetime_value.day)
|
|
55
|
-
rails_time_value = Time.utc(base_date.year, base_date.month, base_date.day, datetime_value.hour, datetime_value.min, datetime_value.sec)
|
|
56
|
-
|
|
57
|
-
[
|
|
58
|
-
{ type: :integer, default: 42 },
|
|
59
|
-
{ type: :string, default: ["'foobar'", ':bazqux'], expected_default: ['foobar', 'bazqux'] },
|
|
60
|
-
{ type: :text, default: nil }, # TEXT columns cannot have a default http://dev.mysql.com/doc/refman/5.7/en/blob.html#idm140380410069472
|
|
61
|
-
{ type: :float, default: 3.14159 },
|
|
62
|
-
{ type: :datetime, default: "'#{datetime_value.strftime('%F %T')}'", expected_default: datetime_value },
|
|
63
|
-
{ type: :time, default: "'#{datetime_value.strftime('%T')}'", expected_default: rails_time_value },
|
|
64
|
-
{ type: :date, default: "'#{datetime_value.strftime('%F')}'", expected_default: date_value },
|
|
65
|
-
{ type: :binary, default: nil }, # BLOB columns cannot have a default http://dev.mysql.com/doc/refman/5.7/en/blob.html#idm140380410069472
|
|
66
|
-
{ type: :boolean, default: [false, true] },
|
|
67
|
-
].each do |test|
|
|
49
|
+
add_column_fixtures.each do |test|
|
|
68
50
|
|
|
69
51
|
defaults = [test[:default]].flatten(1).compact # remove nils for columns that can't have a default
|
|
70
52
|
expected_defaults = test[:expected_default] ? [test[:expected_default]].flatten(1) : defaults
|
|
71
53
|
|
|
72
54
|
should "add a nullable #{test[:type]} column with default null" do
|
|
73
55
|
column_name = "#{@new_column_name}_#{test[:type]}"
|
|
74
|
-
@arguments[0] =
|
|
56
|
+
@arguments[0] = sprintf(test[:command], table: @table_name, column: column_name, default: nil, nullable: true)
|
|
75
57
|
@arguments[-2] = column_name
|
|
76
58
|
@arguments[-1] = { type: test[:type], null: true, default: nil }
|
|
77
59
|
migrate_and_test_field *@arguments
|
|
@@ -80,7 +62,7 @@ class PtOscMigrationIntegrationTest < ActiveRecord::TestCase
|
|
|
80
62
|
defaults.each_with_index do |default, index|
|
|
81
63
|
should "add a nullable #{test[:type]} column with default value #{default}" do
|
|
82
64
|
column_name = "#{@new_column_name}_#{test[:type]}"
|
|
83
|
-
@arguments[0] =
|
|
65
|
+
@arguments[0] = sprintf(test[:command], table: @table_name, column: column_name, default: default, nullable: true)
|
|
84
66
|
@arguments[-2] = column_name
|
|
85
67
|
@arguments[-1] = { type: test[:type], null: true, default: expected_defaults[index] }
|
|
86
68
|
migrate_and_test_field *@arguments
|
|
@@ -88,7 +70,7 @@ class PtOscMigrationIntegrationTest < ActiveRecord::TestCase
|
|
|
88
70
|
|
|
89
71
|
should "add a not-nullable #{test[:type]} column with default value #{default}" do
|
|
90
72
|
column_name = "#{@new_column_name}_#{test[:type]}"
|
|
91
|
-
@arguments[0] =
|
|
73
|
+
@arguments[0] = sprintf(test[:command], table: @table_name, column: column_name, default: default, nullable: false)
|
|
92
74
|
@arguments[-2] = column_name
|
|
93
75
|
@arguments[-1] = { type: test[:type], null: false, default: expected_defaults[index] }
|
|
94
76
|
migrate_and_test_field *@arguments
|
data/test/test_helper.rb
CHANGED
|
@@ -41,6 +41,33 @@ def migrate_and_test_field(command, migration, table_name, field_name, assertion
|
|
|
41
41
|
migration.class.send(:remove_method, :change)
|
|
42
42
|
end
|
|
43
43
|
|
|
44
|
+
# @return [Array<Hash>] Fixtures containing :type, :default, :expected_default, and :command for use with sprintf
|
|
45
|
+
def add_column_fixtures
|
|
46
|
+
# Rails' "magic" time, for Time columns
|
|
47
|
+
# https://github.com/rails/rails/blob/fcf9b712b1dbbcb8f48644e6f20676ad9480ba66/activerecord/lib/active_record/type/time.rb#L16
|
|
48
|
+
base_date = Time.utc(2000, 1, 1, 0, 0, 0)
|
|
49
|
+
|
|
50
|
+
datetime_value = Time.at(Time.now.to_i).utc # Date types we're testing don't have sub-second precision
|
|
51
|
+
date_value = Time.utc(datetime_value.year, datetime_value.month, datetime_value.day)
|
|
52
|
+
rails_time_value = Time.utc(base_date.year, base_date.month, base_date.day, datetime_value.hour, datetime_value.min, datetime_value.sec)
|
|
53
|
+
|
|
54
|
+
fixtures = [
|
|
55
|
+
{ type: :integer, default: 42 },
|
|
56
|
+
{ type: :string, default: ['foobar', :bazqux], expected_default: ['foobar', 'bazqux'] },
|
|
57
|
+
{ type: :text, default: nil }, # TEXT columns cannot have a default http://dev.mysql.com/doc/refman/5.7/en/blob.html#idm140380410069472
|
|
58
|
+
{ type: :float, default: 3.14159 },
|
|
59
|
+
{ type: :datetime, default: datetime_value.strftime('%F %T'), expected_default: datetime_value },
|
|
60
|
+
{ type: :time, default: datetime_value.strftime('%T'), expected_default: rails_time_value },
|
|
61
|
+
{ type: :date, default: datetime_value.strftime('%F'), expected_default: date_value },
|
|
62
|
+
{ type: :binary, default: nil }, # BLOB columns cannot have a default http://dev.mysql.com/doc/refman/5.7/en/blob.html#idm140380410069472
|
|
63
|
+
{ type: :boolean, default: [false, true] },
|
|
64
|
+
]
|
|
65
|
+
fixtures.map do |fixture|
|
|
66
|
+
fixture[:command] = "add_column :%{table}, :%{column}, :#{fixture[:type]}, default: %<default>p, null: %{nullable}"
|
|
67
|
+
fixture
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
44
71
|
# SQLCounter is part of ActiveRecord but is not distributed with the gem (used for internal tests only)
|
|
45
72
|
# see https://github.com/rails/rails/blob/3-2-stable/activerecord/test/cases/helper.rb#L59
|
|
46
73
|
module ActiveRecord
|
|
@@ -7,6 +7,13 @@ class MysqlPtOscAdapterTest < Test::Unit::TestCase
|
|
|
7
7
|
setup do
|
|
8
8
|
TestConnection.establish_connection(test_spec)
|
|
9
9
|
@adapter = TestConnection.connection
|
|
10
|
+
# Silence warnings about not using an ActiveRecord::PtOscMigration
|
|
11
|
+
# @see Kernel#suppress_warnings
|
|
12
|
+
@original_verbosity, $VERBOSE = $VERBOSE, nil
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
teardown do
|
|
16
|
+
$VERBOSE = @original_verbosity
|
|
10
17
|
end
|
|
11
18
|
|
|
12
19
|
context '#rename_table' do
|
|
@@ -32,7 +39,7 @@ class MysqlPtOscAdapterTest < Test::Unit::TestCase
|
|
|
32
39
|
|
|
33
40
|
should 'add an ADD command to the commands hash' do
|
|
34
41
|
table_name = Faker::Lorem.word
|
|
35
|
-
column_name =
|
|
42
|
+
column_name = 'foobar'
|
|
36
43
|
@adapter.add_column(table_name, column_name, :string, default: 0, null: false)
|
|
37
44
|
assert_equal "ADD `#{column_name}` varchar(255) DEFAULT 0 NOT NULL", @adapter.send(:get_commands, table_name).first
|
|
38
45
|
end
|
|
@@ -48,7 +55,7 @@ class MysqlPtOscAdapterTest < Test::Unit::TestCase
|
|
|
48
55
|
context 'with an existing table and column' do
|
|
49
56
|
setup do
|
|
50
57
|
@table_name = Faker::Lorem.word
|
|
51
|
-
@column_name =
|
|
58
|
+
@column_name = 'foobar'
|
|
52
59
|
@adapter.create_table @table_name, force: true do |t|
|
|
53
60
|
t.string @column_name
|
|
54
61
|
end
|
|
@@ -71,14 +78,14 @@ class MysqlPtOscAdapterTest < Test::Unit::TestCase
|
|
|
71
78
|
context 'with an existing table and column' do
|
|
72
79
|
setup do
|
|
73
80
|
@table_name = Faker::Lorem.word
|
|
74
|
-
@column_name =
|
|
81
|
+
@column_name = 'foobar'
|
|
75
82
|
@adapter.create_table @table_name, force: true do |t|
|
|
76
83
|
t.string @column_name, default: nil
|
|
77
84
|
end
|
|
78
85
|
end
|
|
79
86
|
|
|
80
87
|
should 'add a CHANGE command to the commands hash' do
|
|
81
|
-
new_column_name =
|
|
88
|
+
new_column_name = 'foobar'
|
|
82
89
|
@adapter.rename_column(@table_name, @column_name, new_column_name)
|
|
83
90
|
assert_equal "CHANGE `#{@column_name}` `#{new_column_name}` varchar(255) DEFAULT NULL", @adapter.send(:get_commands, @table_name).first
|
|
84
91
|
end
|
|
@@ -94,14 +101,14 @@ class MysqlPtOscAdapterTest < Test::Unit::TestCase
|
|
|
94
101
|
|
|
95
102
|
should 'add a DROP COLUMN command to the commands hash' do
|
|
96
103
|
table_name = Faker::Lorem.word
|
|
97
|
-
column_name =
|
|
104
|
+
column_name = 'foobar'
|
|
98
105
|
@adapter.remove_column(table_name, column_name)
|
|
99
106
|
assert_equal "DROP COLUMN `#{column_name}`", @adapter.send(:get_commands, table_name).first
|
|
100
107
|
end
|
|
101
108
|
|
|
102
109
|
should 'add multiple DROP COLUMN commands to the commands hash' do
|
|
103
110
|
table_name = Faker::Lorem.word
|
|
104
|
-
column_names =
|
|
111
|
+
column_names = %w(foo bar baz)
|
|
105
112
|
@adapter.remove_column(table_name, *column_names)
|
|
106
113
|
commands = @adapter.send(:get_commands, table_name)
|
|
107
114
|
column_names.each_with_index do |column_name, index|
|
|
@@ -120,7 +127,7 @@ class MysqlPtOscAdapterTest < Test::Unit::TestCase
|
|
|
120
127
|
context 'with an existing table and columns' do
|
|
121
128
|
setup do
|
|
122
129
|
@table_name = Faker::Lorem.word
|
|
123
|
-
@column_names =
|
|
130
|
+
@column_names = %w(foo bar baz)
|
|
124
131
|
@adapter.create_table @table_name, force: true do |t|
|
|
125
132
|
@column_names.each do |column_name|
|
|
126
133
|
t.string(column_name, default: nil)
|
|
@@ -157,7 +164,7 @@ class MysqlPtOscAdapterTest < Test::Unit::TestCase
|
|
|
157
164
|
|
|
158
165
|
should 'add a DROP COLUMN command to the commands hash' do
|
|
159
166
|
table_name = Faker::Lorem.word
|
|
160
|
-
index_name = Faker::Lorem.
|
|
167
|
+
index_name = Faker::Lorem.words.join('_')
|
|
161
168
|
@adapter.remove_index!(table_name, index_name)
|
|
162
169
|
assert_equal "DROP INDEX `#{index_name}`", @adapter.send(:get_commands, table_name).first
|
|
163
170
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: pt-osc
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.3
|
|
5
5
|
prerelease:
|
|
6
6
|
platform: ruby
|
|
7
7
|
authors:
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2015-
|
|
12
|
+
date: 2015-03-09 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: bundler
|
|
@@ -248,7 +248,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
248
248
|
version: '0'
|
|
249
249
|
segments:
|
|
250
250
|
- 0
|
|
251
|
-
hash:
|
|
251
|
+
hash: 3650568307132967926
|
|
252
252
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
253
253
|
none: false
|
|
254
254
|
requirements:
|
|
@@ -257,7 +257,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
257
257
|
version: '0'
|
|
258
258
|
segments:
|
|
259
259
|
- 0
|
|
260
|
-
hash:
|
|
260
|
+
hash: 3650568307132967926
|
|
261
261
|
requirements: []
|
|
262
262
|
rubyforge_project:
|
|
263
263
|
rubygems_version: 1.8.23
|