mongify 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,25 +1,58 @@
1
1
  module Mongify
2
2
  # This class is responsible for generating progress bars with status of mongify
3
3
  class Status
4
+ # List of known notifications
5
+ NOTIFICATIONS = ['copy_data', 'copy_embedded', 'copy_polymorphic', 'update_references', 'remove_pre_mongified']
6
+
4
7
  class << self
8
+ #List of all the progress bars.
9
+ def bars
10
+ @bars ||= {}
11
+ end
12
+ #Add a new bar to the list of progress bars
13
+ def add_bar(name, bar)
14
+ self.bars[name] = bar
15
+ end
16
+
5
17
  # Registers the ActiveSupport::Notifications for Mongify
6
18
  def register
7
- @bars = {}
8
19
  ActiveSupport::Notifications.subscribe(/^mongify\./) do |*args|
9
20
  event = ActiveSupport::Notifications::Event.new(*args)
10
- # puts event.name
11
- mongify, name, action = event.name.split('.', 3)
12
- case action
13
- when 'start'
14
- @bars[name] = ProgressBar.new(name.humanize, event.payload[:size] || 0)
15
- when 'inc'
16
- @bars[name].try(:inc)
17
- when 'finish'
18
- @bars[name].try(:finish)
21
+
22
+ action_name = event.name.split('.', 2)[1]
23
+
24
+ if Status::NOTIFICATIONS.include?(action_name)
25
+ case event.payload[:action]
26
+ when 'add'
27
+ self.add_bar(action_name, ProgressBar.new(event.payload[:name], event.payload[:size]))
28
+ when 'inc'
29
+ self.bars[action_name].try(:inc)
30
+ when 'finish'
31
+ self.bars[action_name].try(:finish)
32
+ else
33
+ UI.warn("Unknown Notification Action #{event.payload[:action]}")
34
+ end
35
+ #puts event.payload.inspect
19
36
  else
20
- UI.warn("Unknown Notification Event #{event.name}")
37
+ UI.warn("Unknown Notification Event #{action_name}")
21
38
  end
22
39
  end
40
+
41
+ # Unregisters from {ActiveSupport::Notifications}
42
+ def unregister
43
+ ActiveSupport::Notifications.unsubscribe(/^mongify\./)
44
+ end
45
+
46
+ # Publish an notification event
47
+ # This will publish the event as an mongify.[name]
48
+ # @param [String] name Name of the notification
49
+ # @param [Hash] payload to be sent with the notification
50
+ # @return [nil]
51
+ def publish(name, payload={})
52
+ payload.reverse_merge!(:name => name.humanize, :action => 'inc')
53
+ ActiveSupport::Notifications.instrument("mongify.#{name}", payload)
54
+ nil
55
+ end
23
56
  end
24
57
  end
25
58
  end
@@ -5,7 +5,6 @@ module Mongify
5
5
  #
6
6
  module Process
7
7
  attr_accessor :sql_connection, :no_sql_connection
8
-
9
8
  # Does the actual act of processing the translation.
10
9
  # Takes in boht a sql connection and a no sql connection
11
10
  def process(sql_connection, no_sql_connection)
@@ -13,12 +12,13 @@ module Mongify
13
12
  raise Mongify::NoSqlConnectionRequired, "Can only write to Mongify::Database::NoSqlConnection" unless no_sql_connection.is_a?(Mongify::Database::NoSqlConnection)
14
13
 
15
14
  self.sql_connection = sql_connection
16
- raise "SQL Connection is not valid" unless self.sql_connection.valid?
15
+ raise SqlConnectionInvalid, "SQL Connection is not valid" unless self.sql_connection.valid?
17
16
  self.no_sql_connection = no_sql_connection
18
- raise "noSql Connection is not valid" unless self.no_sql_connection.valid?
17
+ raise NoSqlConnectionInvalid, "noSql Connection is not valid" unless self.no_sql_connection.valid?
19
18
 
20
19
  no_sql_connection.ask_to_drop_database if no_sql_connection.forced?
21
20
 
21
+ setup_db_index
22
22
  copy_data
23
23
  copy_embedded_tables
24
24
  update_reference_ids
@@ -31,23 +31,32 @@ module Mongify
31
31
  private
32
32
  #######
33
33
 
34
+ # Setups up pre_mongifed_id as an index to speed up lookup performance
35
+ def setup_db_index
36
+ self.copy_tables.each do |t|
37
+ no_sql_connection.create_pre_mongified_id_index(t.name)
38
+ end
39
+ end
40
+
34
41
  # Does the straight copy (of tables)
35
42
  def copy_data
36
- ActiveSupport::Notifications.instrument('mongify.copy_data.start', :size => self.copy_tables.count)
37
43
  self.copy_tables.each do |t|
38
- sql_connection.select_rows(t.sql_name).each do |row|
44
+ rows = sql_connection.select_rows(t.sql_name)
45
+ Mongify::Status.publish('copy_data', :size => rows.count, :name => "Copying #{t.name}", :action => 'add')
46
+ rows.each do |row|
39
47
  no_sql_connection.insert_into(t.name, t.translate(row))
48
+ Mongify::Status.publish('copy_data')
40
49
  end
41
- ActiveSupport::Notifications.instrument('mongify.copy_data.inc')
50
+ Mongify::Status.publish('copy_data', :action => 'finish')
42
51
  end
43
- ActiveSupport::Notifications.instrument('mongify.copy_data.finish')
44
52
  end
45
53
 
46
54
  # Does a copy of the embedded tables
47
55
  def copy_embedded_tables
48
- ActiveSupport::Notifications.instrument('mongify.copy_embedded_tables.start', :size => self.embed_tables.count)
49
56
  self.embed_tables.each do |t|
50
- sql_connection.select_rows(t.sql_name).each do |row|
57
+ rows = sql_connection.select_rows(t.sql_name)
58
+ Mongify::Status.publish('copy_embedded', :size => rows.count, :name => "Embedding #{t.name}", :action => 'add')
59
+ rows.each do |row|
51
60
  target_row = no_sql_connection.find_one(t.embed_in, {:pre_mongified_id => row[t.embed_on]})
52
61
  next unless target_row.present?
53
62
  row = t.translate(row)
@@ -56,54 +65,60 @@ module Mongify
56
65
  row.delete('pre_mongified_id')
57
66
  save_function_call = t.embedded_as_object? ? '$set' : '$addToSet'
58
67
  no_sql_connection.update(t.embed_in, target_row['_id'], {save_function_call => {t.name => row}})
68
+ Mongify::Status.publish('copy_embedded')
59
69
  end
60
- ActiveSupport::Notifications.instrument('mongify.copy_embedded_tables.inc')
70
+ Mongify::Status.publish('copy_embedded', :action => 'finish')
61
71
  end
62
- ActiveSupport::Notifications.instrument('mongify.copy_embedded_tables.finish')
63
72
  end
64
73
 
65
74
  # Moves over polymorphic data
66
75
  def copy_polymorphic_tables
67
- ActiveSupport::Notifications.instrument('mongify.copy_polymorphic_tables.start', :size => self.polymorphic_tables.count)
68
76
  self.polymorphic_tables.each do |t|
69
77
  polymorphic_id_col, polymorphic_type_col = "#{t.polymorphic_as}_id", "#{t.polymorphic_as}_type"
70
- sql_connection.select_rows(t.sql_name).each do |row|
71
- table_name = row[polymorphic_type_col].tableize
72
- new_id = no_sql_connection.get_id_using_pre_mongified_id(table_name, row[polymorphic_id_col])
73
- if new_id
74
- row = t.translate(row)
75
- row.merge!(fetch_reference_ids(t, row))
76
- row[polymorphic_id_col] = new_id
77
- row.delete('pre_mongified_id')
78
- if t.embedded?
79
- row.delete(polymorphic_id_col)
80
- row.delete(polymorphic_type_col)
81
- save_function_call = t.embedded_as_object? ? '$set' : '$addToSet'
82
- no_sql_connection.update(table_name, new_id, {save_function_call => {t.name => row}})
83
- else
84
- no_sql_connection.insert_into(t.name, row)
85
- end
78
+ rows = sql_connection.select_rows(t.sql_name)
79
+ Mongify::Status.publish('copy_polymorphic', :size => rows.count, :name => "Polymorphicizing #{t.name}", :action => 'add')
80
+ rows.each do |row|
81
+
82
+ #If no data is in the column, skip importing
83
+
84
+ if (row[polymorphic_type_col])
85
+ table_name = row[polymorphic_type_col].tableize
86
+ new_id = no_sql_connection.get_id_using_pre_mongified_id(table_name, row[polymorphic_id_col])
87
+ end
88
+
89
+ row = t.translate(row)
90
+ row[polymorphic_id_col] = new_id if new_id
91
+ row.merge!(fetch_reference_ids(t, row))
92
+ row.delete('pre_mongified_id')
93
+
94
+ if t.embedded? && table_name
95
+ row.delete(polymorphic_id_col)
96
+ row.delete(polymorphic_type_col)
97
+ save_function_call = t.embedded_as_object? ? '$set' : '$addToSet'
98
+ no_sql_connection.update(table_name, new_id, {save_function_call => {t.name => row}})
86
99
  else
87
- UI.warn "#{table_name} table not found on #{t.sql_name} polymorphic import"
100
+ no_sql_connection.insert_into(t.name, row)
88
101
  end
102
+
103
+ Mongify::Status.publish('copy_polymorphic')
89
104
  end
90
- ActiveSupport::Notifications.instrument('mongify.copy_polymorphic_tables.inc')
105
+ Mongify::Status.publish('copy_polymorphic', :action => 'finish')
91
106
  end
92
- ActiveSupport::Notifications.instrument('mongify.copy_polymorphic_tables.finish')
93
107
  end
94
108
 
95
109
  # Updates the reference ids in the no sql database
96
110
  def update_reference_ids
97
- ActiveSupport::Notifications.instrument('mongify.update_reference_ids.start', :size => self.tables.size)
98
- self.tables.each do |t|
99
- no_sql_connection.select_rows(t.name).each do |row|
111
+ self.copy_tables.each do |t|
112
+ rows = no_sql_connection.select_rows(t.name)
113
+ Mongify::Status.publish('update_references', :size => rows.count, :name => "Updating References #{t.name}", :action => 'add')
114
+ rows.each do |row|
100
115
  id = row["_id"]
101
116
  attributes = fetch_reference_ids(t, row)
102
117
  no_sql_connection.update(t.name, id, {"$set" => attributes}) unless attributes.blank?
118
+ Mongify::Status.publish('update_references')
103
119
  end
104
- ActiveSupport::Notifications.instrument('mongify.update_reference_ids.inc')
120
+ Mongify::Status.publish('update_references', :action => 'finish')
105
121
  end
106
- ActiveSupport::Notifications.instrument('mongify.update_reference_ids.finish')
107
122
  end
108
123
 
109
124
  # Fetches the new _id from a collection
@@ -118,13 +133,12 @@ module Mongify
118
133
 
119
134
  # Removes 'pre_mongiifed_id's from all collection
120
135
  def remove_pre_mongified_ids
121
- ActiveSupport::Notifications.instrument('mongify.remove_pre_mongified_ids.start', :size => self.tables.size)
122
- p = ProgressBar.new("Removed pre_mongified_ids", self.copy_tables.count)
123
136
  self.copy_tables.each do |t|
137
+ Mongify::Status.publish('remove_pre_mongified', :size => 1, :name => "Removing pre_mongified_id #{t.name}", :action => 'add')
124
138
  no_sql_connection.remove_pre_mongified_ids(t.name)
125
- ActiveSupport::Notifications.instrument('mongify.remove_pre_mongified_ids.inc')
139
+ Mongify::Status.publish('remove_pre_mongified', :action => 'finish')
140
+ # Mongify::Status.publish('remove_pre_mongified')
126
141
  end
127
- ActiveSupport::Notifications.instrument('mongify.remove_pre_mongified_ids.finish')
128
142
  end
129
143
 
130
144
  end
data/lib/mongify/ui.rb CHANGED
@@ -49,6 +49,14 @@ module Mongify
49
49
  def out_stream
50
50
  Configuration.out_stream
51
51
  end
52
+
53
+ # Creates an instance of HighLine
54
+ # which lets us figure out width of console
55
+ # plus a whole lot more
56
+ # @reutrn [HighLine] instance
57
+ def terminal_helper
58
+ @terminal_helper ||= HighLine.new
59
+ end
52
60
  end
53
61
  end
54
62
  end
@@ -1,4 +1,4 @@
1
1
  module Mongify
2
2
  # Mongify's Current Version Number
3
- VERSION = "0.1.2"
3
+ VERSION = "0.1.3"
4
4
  end
data/mongify.gemspec CHANGED
@@ -19,7 +19,7 @@ Gem::Specification.new do |s|
19
19
  s.add_dependency('mongo', ">= 1.1.5")
20
20
  s.add_dependency('bson_ext', ">= 1.1.5")
21
21
  s.add_dependency('net-ssh', ">= 2.0")
22
- s.add_dependency('progressbar', ">= 0.9")
22
+ s.add_dependency('highline', '>= 1.6.1')
23
23
 
24
24
  s.add_development_dependency('rspec', '>= 2.0')
25
25
  s.add_development_dependency('rcov', '>= 0.9.9')
@@ -26,10 +26,10 @@ describe Mongify::Database::BaseConnection do
26
26
  end
27
27
 
28
28
  it "should raise error when trying to call has_connection?" do
29
- lambda { @base_connection.has_connection? }.should raise_error(NotImplementedError)
29
+ lambda { @base_connection.has_connection? }.should raise_error(Mongify::NotImplementedMongifyError)
30
30
  end
31
31
  it "should raise error when trying to call setup_connection_adapter" do
32
- lambda { @base_connection.setup_connection_adapter}.should raise_error(NotImplementedError)
32
+ lambda { @base_connection.setup_connection_adapter}.should raise_error(Mongify::NotImplementedMongifyError)
33
33
  end
34
34
 
35
35
  it "should raise error on setting unknown variable setting" do
@@ -122,6 +122,35 @@ describe Mongify::Database::Column do
122
122
  end
123
123
  end
124
124
 
125
+ context "as" do
126
+ subject {Mongify::Database::Column.new('total', :decimal)}
127
+ it "should default to string" do
128
+ subject.as.should == 'string'
129
+ end
130
+ it "should allow it to be set to integer" do
131
+ subject.as = 'integer'
132
+ subject.should be_as_integer
133
+ end
134
+ it "should not allow other values" do
135
+ subject.as = "zuza"
136
+ subject.as.should == 'string'
137
+ end
138
+ end
139
+ context "scale" do
140
+ subject {Mongify::Database::Column.new('total', :decimal, :as => 'integer')}
141
+ it "should be defaulted to 0" do
142
+ subject.scale.should be_zero
143
+ end
144
+ it "should let you set the scale" do
145
+ subject.scale = 3
146
+ subject.scale.should == 3
147
+ end
148
+ it "should return 0 on invalid input" do
149
+ subject.scale = 'zuza'
150
+ subject.scale.should be_zero
151
+ end
152
+ end
153
+
125
154
  context :to_print do
126
155
  before(:each) do
127
156
  @column = Mongify::Database::Column.new('first_name', :string)
@@ -160,6 +189,10 @@ describe Mongify::Database::Column do
160
189
  @column = Mongify::Database::Column.new('id', :key)
161
190
  @column.translate(123123).should == {"pre_mongified_id" => 123123}
162
191
  end
192
+ it "should return an integer for pre_mongified_id" do
193
+ @column = Mongify::Database::Column.new('id', :key)
194
+ @column.translate('123123').should == {"pre_mongified_id" => 123123}
195
+ end
163
196
  end
164
197
  context :type_cast do
165
198
  it "should return value if unknown type" do
@@ -212,14 +245,42 @@ describe Mongify::Database::Column do
212
245
  before(:each) do
213
246
  @column = Mongify::Database::Column.new('price', :decimal)
214
247
  end
215
- it "should convert numbers to decimal" do
216
- @column.send(:type_cast, 101.43).should == BigDecimal.new("101.43")
248
+ it "should convert numbers to decimal string" do
249
+ @column.send(:type_cast, 101.43).should == "101.43"
217
250
  end
218
- it "should convert integers to decimal" do
219
- @column.send(:type_cast, 101).should == BigDecimal.new("101.0")
251
+ it "should convert integers to decimal string" do
252
+ @column.send(:type_cast, 101).should == "101.0"
220
253
  end
221
- it "should convert strings to 0.0" do
222
- @column.send(:type_cast, 'zuza').should == BigDecimal.new("0")
254
+ it "should convert strings to 0.0 (string)" do
255
+ @column.send(:type_cast, 'zuza').should == "0.0"
256
+ end
257
+ it "should return a string value" do
258
+ @column.send(:type_cast, 101.43).should be_a_kind_of String
259
+ end
260
+
261
+ context "as integer" do
262
+ before(:each) do
263
+ @column = Mongify::Database::Column.new('price', :decimal, :as => 'integer')
264
+ @value = 101.123455
265
+ end
266
+ it "should be as_integer" do
267
+ @column.should be_as_integer
268
+ end
269
+ it "should convert number to integer" do
270
+ @column.send(:type_cast, @value).should == 101
271
+ end
272
+ it "should let you specify scale" do
273
+ @column.scale = 3
274
+ @column.send(:type_cast, @value).should == 101123
275
+ end
276
+ it "should round correctly to specified scale" do
277
+ @column.scale = 4
278
+ @column.send(:type_cast, @value).should == 1011235
279
+ end
280
+ it "should return an integer value" do
281
+ @column.scale = 3
282
+ @column.send(:type_cast, @value).should be_an_integer
283
+ end
223
284
  end
224
285
  end
225
286
  context :timestamp do
@@ -116,9 +116,23 @@ describe Mongify::Database::NoSqlConnection do
116
116
  end
117
117
  end
118
118
 
119
+ it "should create index for pre_mongified_id" do
120
+ @collection.should_receive(:create_index).with([["pre_mongified_id", Mongo::ASCENDING]]).and_return(true)
121
+ @mongodb_connection.create_pre_mongified_id_index('users')
122
+ end
123
+
119
124
  context "remove_pre_mongified_ids" do
125
+ before(:each) do
126
+ @collection.stub(:index_information).and_return('pre_mongified_id_1' => 'something')
127
+ end
120
128
  it "should call update with unset" do
121
129
  @collection.should_receive(:update).with({},{'$unset' => {'pre_mongified_id' => 1}}, {:multi=>true})
130
+ @collection.stub(:drop_index)
131
+ @mongodb_connection.remove_pre_mongified_ids('users')
132
+ end
133
+ it "should drop the index" do
134
+ @collection.should_receive(:drop_index).with('pre_mongified_id_1')
135
+ @collection.stub(:update)
122
136
  @mongodb_connection.remove_pre_mongified_ids('users')
123
137
  end
124
138
  end
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+
3
+ describe Mongify::Status do
4
+ it "should warn if it's an unknown notification" do
5
+ Mongify::UI.should_receive(:warn).at_least(1)
6
+ Mongify::Status.publish('unknwon')
7
+ end
8
+ context "publish" do
9
+ before(:each) do
10
+ @bar = stub(:title= => '')
11
+ Mongify::Status.bars.stub(:[]).and_return(@bar)
12
+ end
13
+ it "should create a new progress bar" do
14
+ Mongify::ProgressBar.should_receive(:new)
15
+ Mongify::Status.publish('copy_data', :size => 100, :action => 'add')
16
+ end
17
+ context "on inc" do
18
+ before(:each) do
19
+ @bar.stub(:inc)
20
+ end
21
+ it "should inc a already created progress bar" do
22
+ @bar.should_receive(:inc)
23
+ Mongify::Status.publish('copy_data', :action => 'inc')
24
+ end
25
+ it "should inc by default action" do
26
+ @bar.should_receive(:inc)
27
+ Mongify::Status.publish('copy_data')
28
+ end
29
+ end
30
+ it "should finish the progress bar" do
31
+ @bar.should_receive(:finish)
32
+ Mongify::Status.publish('copy_data', :action => 'finish')
33
+ end
34
+ end
35
+ end
@@ -23,6 +23,10 @@ describe Mongify::Translation::Process do
23
23
  @translation.stub(:copy_data)
24
24
  @translation.stub(:copy_embedded_tables)
25
25
  end
26
+ it "should setup index on pre_mongify_id" do
27
+ @translation.should_receive(:setup_db_index)
28
+ @translation.process(@sql_connection, @no_sql_connection)
29
+ end
26
30
  it "should call copy_data" do
27
31
  @translation.should_receive(:copy_data)
28
32
  @translation.process(@sql_connection, @no_sql_connection)
@@ -39,6 +43,13 @@ describe Mongify::Translation::Process do
39
43
  @translation.should_receive(:remove_pre_mongified_ids)
40
44
  @translation.process(@sql_connection, @no_sql_connection)
41
45
  end
46
+
47
+ it "should add pre_mongified_id index to database" do
48
+ tables = [stub(:name => 'users')]
49
+ @translation.stub(:copy_tables).and_return(tables)
50
+ @no_sql_connection.should_receive(:create_pre_mongified_id_index).with('users')
51
+ @translation.process(@sql_connection, @no_sql_connection)
52
+ end
42
53
  end
43
54
 
44
55
  it "should ask_to_drop_database if mongodb_connection is forced" do
@@ -47,7 +58,6 @@ describe Mongify::Translation::Process do
47
58
  @translation.process(@sql_connection, @no_sql_connection)
48
59
  end
49
60
 
50
-
51
61
  context "fetch_reference_ids" do
52
62
  it "should get correct information" do
53
63
  @no_sql_connection = mock()
@@ -153,8 +163,8 @@ describe Mongify::Translation::Process do
153
163
  :sql_name => 'user_accounts')
154
164
  @translation.stub(:find).with('user_accounts').and_return([@ref_table])
155
165
 
156
- @sql_connection.should_receive(:select_rows).with('comments').and_return([{'commentable_id' => 1, 'commentable_type' => 'UserAccount', 'data' => 'good'}])
157
- @no_sql_connection.should_receive(:get_id_using_pre_mongified_id).with('user_accounts', 1).and_return(500)
166
+ @sql_connection.stub(:select_rows).with('comments').and_return([{'commentable_id' => 1, 'commentable_type' => 'UserAccount', 'data' => 'good'}])
167
+ @no_sql_connection.stub(:get_id_using_pre_mongified_id).with('user_accounts', 1).and_return(500)
158
168
  end
159
169
  context "embedded" do
160
170
  it "should work correctly" do
@@ -187,10 +197,27 @@ describe Mongify::Translation::Process do
187
197
  :reference_columns => [])
188
198
 
189
199
  @translation.stub(:all_tables).and_return([@table])
190
-
200
+ @no_sql_connection.should_receive(:get_id_using_pre_mongified_id).with('user_accounts', 1).and_return(500)
191
201
  @no_sql_connection.should_receive(:insert_into).with('comments', {'data' => 123, 'commentable_type' => 'UserAccount', 'commentable_id' => 500})
192
202
  @translation.send(:copy_polymorphic_tables)
193
203
  end
204
+ it "should copy even if there is no polymorphic data" do
205
+ @table = mock(:translate => {'data' => 123, 'commentable_type' => nil, 'commentable_id' => nil},
206
+ :name => 'comments',
207
+ :embedded? => false,
208
+ :polymorphic_as => 'commentable',
209
+ :polymorphic? => true,
210
+ :ignored? => false,
211
+ :embedded_as_object? => false,
212
+ :sql_name => 'comments',
213
+ :reference_columns => [])
214
+
215
+ @translation.stub(:all_tables).and_return([@table])
216
+ @sql_connection.should_receive(:select_rows).with('comments').and_return([{'commentable_id' => nil, 'commentable_type' => nil, 'data' => 'good'}])
217
+ @no_sql_connection.should_receive(:insert_into).with('comments', {'data' => 123, 'commentable_type' => nil, 'commentable_id' => nil})
218
+ @no_sql_connection.should_receive(:get_id_using_pre_mongified_id).never
219
+ @translation.send(:copy_polymorphic_tables)
220
+ end
194
221
  end
195
222
  end
196
223