dump 1.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.
Files changed (89) hide show
  1. data/.autotest +13 -0
  2. data/.gitignore +12 -0
  3. data/LICENSE.txt +20 -0
  4. data/README.markdown +250 -0
  5. data/dump.gemspec +22 -0
  6. data/lib/dump.rb +3 -0
  7. data/lib/dump/capistrano.rb +1 -0
  8. data/lib/dump/railtie.rb +8 -0
  9. data/lib/dump_rake.rb +85 -0
  10. data/lib/dump_rake/archive_tar_minitar_fix.rb +8 -0
  11. data/lib/dump_rake/assets.rb +22 -0
  12. data/lib/dump_rake/continious_timeout.rb +38 -0
  13. data/lib/dump_rake/dump.rb +175 -0
  14. data/lib/dump_rake/dump_reader.rb +289 -0
  15. data/lib/dump_rake/dump_writer.rb +119 -0
  16. data/lib/dump_rake/env.rb +139 -0
  17. data/lib/dump_rake/env/filter.rb +26 -0
  18. data/lib/dump_rake/rails_root.rb +12 -0
  19. data/lib/dump_rake/table_manipulation.rb +131 -0
  20. data/lib/generators/assets_config/assets_config_generator.rb +16 -0
  21. data/lib/generators/assets_config/templates/assets +8 -0
  22. data/lib/tasks/assets.rake +17 -0
  23. data/lib/tasks/dump.rake +27 -0
  24. data/recipes/dump.rb +343 -0
  25. data/script/update_readme +21 -0
  26. data/spec/.gitignore +1 -0
  27. data/spec/.tmignore +1 -0
  28. data/spec/cycle_spec.rb +229 -0
  29. data/spec/db/database.example.yml +19 -0
  30. data/spec/db/schema.rb +7 -0
  31. data/spec/dummy-3.1.3/.gitignore +15 -0
  32. data/spec/dummy-3.1.3/.rspec +1 -0
  33. data/spec/dummy-3.1.3/Gemfile +23 -0
  34. data/spec/dummy-3.1.3/Gemfile.lock +159 -0
  35. data/spec/dummy-3.1.3/README +261 -0
  36. data/spec/dummy-3.1.3/Rakefile +7 -0
  37. data/spec/dummy-3.1.3/app/assets/images/rails.png +0 -0
  38. data/spec/dummy-3.1.3/app/assets/javascripts/application.js +9 -0
  39. data/spec/dummy-3.1.3/app/assets/stylesheets/application.css +7 -0
  40. data/spec/dummy-3.1.3/app/controllers/application_controller.rb +3 -0
  41. data/spec/dummy-3.1.3/app/helpers/application_helper.rb +2 -0
  42. data/spec/dummy-3.1.3/app/mailers/.gitkeep +0 -0
  43. data/spec/dummy-3.1.3/app/models/.gitkeep +0 -0
  44. data/spec/dummy-3.1.3/app/views/layouts/application.html.erb +14 -0
  45. data/spec/dummy-3.1.3/config.ru +4 -0
  46. data/spec/dummy-3.1.3/config/application.rb +54 -0
  47. data/spec/dummy-3.1.3/config/boot.rb +6 -0
  48. data/spec/dummy-3.1.3/config/database.yml +25 -0
  49. data/spec/dummy-3.1.3/config/environment.rb +5 -0
  50. data/spec/dummy-3.1.3/config/environments/development.rb +30 -0
  51. data/spec/dummy-3.1.3/config/environments/production.rb +60 -0
  52. data/spec/dummy-3.1.3/config/environments/test.rb +39 -0
  53. data/spec/dummy-3.1.3/config/initializers/backtrace_silencers.rb +7 -0
  54. data/spec/dummy-3.1.3/config/initializers/inflections.rb +10 -0
  55. data/spec/dummy-3.1.3/config/initializers/mime_types.rb +5 -0
  56. data/spec/dummy-3.1.3/config/initializers/secret_token.rb +7 -0
  57. data/spec/dummy-3.1.3/config/initializers/session_store.rb +8 -0
  58. data/spec/dummy-3.1.3/config/initializers/wrap_parameters.rb +14 -0
  59. data/spec/dummy-3.1.3/config/locales/en.yml +5 -0
  60. data/spec/dummy-3.1.3/config/routes.rb +58 -0
  61. data/spec/dummy-3.1.3/db/seeds.rb +7 -0
  62. data/spec/dummy-3.1.3/doc/README_FOR_APP +2 -0
  63. data/spec/dummy-3.1.3/lib/assets/.gitkeep +0 -0
  64. data/spec/dummy-3.1.3/lib/tasks/.gitkeep +0 -0
  65. data/spec/dummy-3.1.3/log/.gitkeep +0 -0
  66. data/spec/dummy-3.1.3/public/404.html +26 -0
  67. data/spec/dummy-3.1.3/public/422.html +26 -0
  68. data/spec/dummy-3.1.3/public/500.html +26 -0
  69. data/spec/dummy-3.1.3/public/favicon.ico +0 -0
  70. data/spec/dummy-3.1.3/public/index.html +241 -0
  71. data/spec/dummy-3.1.3/public/robots.txt +5 -0
  72. data/spec/dummy-3.1.3/script/rails +6 -0
  73. data/spec/dummy-3.1.3/spec/spec_helper.rb +32 -0
  74. data/spec/dummy-3.1.3/vendor/assets/stylesheets/.gitkeep +0 -0
  75. data/spec/dummy-3.1.3/vendor/plugins/.gitkeep +0 -0
  76. data/spec/lib/dump_rake/dump_reader_spec.rb +638 -0
  77. data/spec/lib/dump_rake/dump_spec.rb +291 -0
  78. data/spec/lib/dump_rake/dump_writer_spec.rb +328 -0
  79. data/spec/lib/dump_rake/env/filter_spec.rb +56 -0
  80. data/spec/lib/dump_rake/env_spec.rb +139 -0
  81. data/spec/lib/dump_rake/rails_root_spec.rb +45 -0
  82. data/spec/lib/dump_rake/table_manipulation_spec.rb +256 -0
  83. data/spec/lib/dump_rake_spec.rb +326 -0
  84. data/spec/recipes/dump_spec.rb +553 -0
  85. data/spec/spec.opts +4 -0
  86. data/spec/spec_helper.rb +34 -0
  87. data/spec/tasks/assets_spec.rb +92 -0
  88. data/spec/tasks/dump_spec.rb +107 -0
  89. metadata +272 -0
@@ -0,0 +1,26 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>The change you wanted was rejected (422)</title>
5
+ <style type="text/css">
6
+ body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
7
+ div.dialog {
8
+ width: 25em;
9
+ padding: 0 4em;
10
+ margin: 4em auto 0 auto;
11
+ border: 1px solid #ccc;
12
+ border-right-color: #999;
13
+ border-bottom-color: #999;
14
+ }
15
+ h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
16
+ </style>
17
+ </head>
18
+
19
+ <body>
20
+ <!-- This file lives in public/422.html -->
21
+ <div class="dialog">
22
+ <h1>The change you wanted was rejected.</h1>
23
+ <p>Maybe you tried to change something you didn't have access to.</p>
24
+ </div>
25
+ </body>
26
+ </html>
@@ -0,0 +1,26 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>We're sorry, but something went wrong (500)</title>
5
+ <style type="text/css">
6
+ body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
7
+ div.dialog {
8
+ width: 25em;
9
+ padding: 0 4em;
10
+ margin: 4em auto 0 auto;
11
+ border: 1px solid #ccc;
12
+ border-right-color: #999;
13
+ border-bottom-color: #999;
14
+ }
15
+ h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
16
+ </style>
17
+ </head>
18
+
19
+ <body>
20
+ <!-- This file lives in public/500.html -->
21
+ <div class="dialog">
22
+ <h1>We're sorry, but something went wrong.</h1>
23
+ <p>We've been notified about this issue and we'll take a look at it shortly.</p>
24
+ </div>
25
+ </body>
26
+ </html>
File without changes
@@ -0,0 +1,241 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Ruby on Rails: Welcome aboard</title>
5
+ <style type="text/css" media="screen">
6
+ body {
7
+ margin: 0;
8
+ margin-bottom: 25px;
9
+ padding: 0;
10
+ background-color: #f0f0f0;
11
+ font-family: "Lucida Grande", "Bitstream Vera Sans", "Verdana";
12
+ font-size: 13px;
13
+ color: #333;
14
+ }
15
+
16
+ h1 {
17
+ font-size: 28px;
18
+ color: #000;
19
+ }
20
+
21
+ a {color: #03c}
22
+ a:hover {
23
+ background-color: #03c;
24
+ color: white;
25
+ text-decoration: none;
26
+ }
27
+
28
+
29
+ #page {
30
+ background-color: #f0f0f0;
31
+ width: 750px;
32
+ margin: 0;
33
+ margin-left: auto;
34
+ margin-right: auto;
35
+ }
36
+
37
+ #content {
38
+ float: left;
39
+ background-color: white;
40
+ border: 3px solid #aaa;
41
+ border-top: none;
42
+ padding: 25px;
43
+ width: 500px;
44
+ }
45
+
46
+ #sidebar {
47
+ float: right;
48
+ width: 175px;
49
+ }
50
+
51
+ #footer {
52
+ clear: both;
53
+ }
54
+
55
+ #header, #about, #getting-started {
56
+ padding-left: 75px;
57
+ padding-right: 30px;
58
+ }
59
+
60
+
61
+ #header {
62
+ background-image: url("/assets/rails.png");
63
+ background-repeat: no-repeat;
64
+ background-position: top left;
65
+ height: 64px;
66
+ }
67
+ #header h1, #header h2 {margin: 0}
68
+ #header h2 {
69
+ color: #888;
70
+ font-weight: normal;
71
+ font-size: 16px;
72
+ }
73
+
74
+
75
+ #about h3 {
76
+ margin: 0;
77
+ margin-bottom: 10px;
78
+ font-size: 14px;
79
+ }
80
+
81
+ #about-content {
82
+ background-color: #ffd;
83
+ border: 1px solid #fc0;
84
+ margin-left: -55px;
85
+ margin-right: -10px;
86
+ }
87
+ #about-content table {
88
+ margin-top: 10px;
89
+ margin-bottom: 10px;
90
+ font-size: 11px;
91
+ border-collapse: collapse;
92
+ }
93
+ #about-content td {
94
+ padding: 10px;
95
+ padding-top: 3px;
96
+ padding-bottom: 3px;
97
+ }
98
+ #about-content td.name {color: #555}
99
+ #about-content td.value {color: #000}
100
+
101
+ #about-content ul {
102
+ padding: 0;
103
+ list-style-type: none;
104
+ }
105
+
106
+ #about-content.failure {
107
+ background-color: #fcc;
108
+ border: 1px solid #f00;
109
+ }
110
+ #about-content.failure p {
111
+ margin: 0;
112
+ padding: 10px;
113
+ }
114
+
115
+
116
+ #getting-started {
117
+ border-top: 1px solid #ccc;
118
+ margin-top: 25px;
119
+ padding-top: 15px;
120
+ }
121
+ #getting-started h1 {
122
+ margin: 0;
123
+ font-size: 20px;
124
+ }
125
+ #getting-started h2 {
126
+ margin: 0;
127
+ font-size: 14px;
128
+ font-weight: normal;
129
+ color: #333;
130
+ margin-bottom: 25px;
131
+ }
132
+ #getting-started ol {
133
+ margin-left: 0;
134
+ padding-left: 0;
135
+ }
136
+ #getting-started li {
137
+ font-size: 18px;
138
+ color: #888;
139
+ margin-bottom: 25px;
140
+ }
141
+ #getting-started li h2 {
142
+ margin: 0;
143
+ font-weight: normal;
144
+ font-size: 18px;
145
+ color: #333;
146
+ }
147
+ #getting-started li p {
148
+ color: #555;
149
+ font-size: 13px;
150
+ }
151
+
152
+
153
+ #sidebar ul {
154
+ margin-left: 0;
155
+ padding-left: 0;
156
+ }
157
+ #sidebar ul h3 {
158
+ margin-top: 25px;
159
+ font-size: 16px;
160
+ padding-bottom: 10px;
161
+ border-bottom: 1px solid #ccc;
162
+ }
163
+ #sidebar li {
164
+ list-style-type: none;
165
+ }
166
+ #sidebar ul.links li {
167
+ margin-bottom: 5px;
168
+ }
169
+
170
+ .filename {
171
+ font-style: italic;
172
+ }
173
+ </style>
174
+ <script type="text/javascript">
175
+ function about() {
176
+ info = document.getElementById('about-content');
177
+ if (window.XMLHttpRequest)
178
+ { xhr = new XMLHttpRequest(); }
179
+ else
180
+ { xhr = new ActiveXObject("Microsoft.XMLHTTP"); }
181
+ xhr.open("GET","rails/info/properties",false);
182
+ xhr.send("");
183
+ info.innerHTML = xhr.responseText;
184
+ info.style.display = 'block'
185
+ }
186
+ </script>
187
+ </head>
188
+ <body>
189
+ <div id="page">
190
+ <div id="sidebar">
191
+ <ul id="sidebar-items">
192
+ <li>
193
+ <h3>Browse the documentation</h3>
194
+ <ul class="links">
195
+ <li><a href="http://guides.rubyonrails.org/">Rails Guides</a></li>
196
+ <li><a href="http://api.rubyonrails.org/">Rails API</a></li>
197
+ <li><a href="http://www.ruby-doc.org/core/">Ruby core</a></li>
198
+ <li><a href="http://www.ruby-doc.org/stdlib/">Ruby standard library</a></li>
199
+ </ul>
200
+ </li>
201
+ </ul>
202
+ </div>
203
+
204
+ <div id="content">
205
+ <div id="header">
206
+ <h1>Welcome aboard</h1>
207
+ <h2>You&rsquo;re riding Ruby on Rails!</h2>
208
+ </div>
209
+
210
+ <div id="about">
211
+ <h3><a href="rails/info/properties" onclick="about(); return false">About your application&rsquo;s environment</a></h3>
212
+ <div id="about-content" style="display: none"></div>
213
+ </div>
214
+
215
+ <div id="getting-started">
216
+ <h1>Getting started</h1>
217
+ <h2>Here&rsquo;s how to get rolling:</h2>
218
+
219
+ <ol>
220
+ <li>
221
+ <h2>Use <code>rails generate</code> to create your models and controllers</h2>
222
+ <p>To see all available options, run it without parameters.</p>
223
+ </li>
224
+
225
+ <li>
226
+ <h2>Set up a default route and remove <span class="filename">public/index.html</span></h2>
227
+ <p>Routes are set up in <span class="filename">config/routes.rb</span>.</p>
228
+ </li>
229
+
230
+ <li>
231
+ <h2>Create your database</h2>
232
+ <p>Run <code>rake db:create</code> to create your database. If you're not using SQLite (the default), edit <span class="filename">config/database.yml</span> with your username and password.</p>
233
+ </li>
234
+ </ol>
235
+ </div>
236
+ </div>
237
+
238
+ <div id="footer">&nbsp;</div>
239
+ </div>
240
+ </body>
241
+ </html>
@@ -0,0 +1,5 @@
1
+ # See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file
2
+ #
3
+ # To ban all spiders from the entire site uncomment the next two lines:
4
+ # User-Agent: *
5
+ # Disallow: /
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
3
+
4
+ APP_PATH = File.expand_path('../../config/application', __FILE__)
5
+ require File.expand_path('../../config/boot', __FILE__)
6
+ require 'rails/commands'
@@ -0,0 +1,32 @@
1
+ # This file is copied to spec/ when you run 'rails generate rspec:install'
2
+ ENV["RAILS_ENV"] ||= 'test'
3
+ require File.expand_path("../../config/environment", __FILE__)
4
+ require 'rspec/rails'
5
+ require 'rspec/autorun'
6
+
7
+ # Requires supporting ruby files with custom matchers and macros, etc,
8
+ # in spec/support/ and its subdirectories.
9
+ Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
10
+
11
+ RSpec.configure do |config|
12
+ # ## Mock Framework
13
+ #
14
+ # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
15
+ #
16
+ # config.mock_with :mocha
17
+ # config.mock_with :flexmock
18
+ # config.mock_with :rr
19
+
20
+ # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
21
+ config.fixture_path = "#{::Rails.root}/spec/fixtures"
22
+
23
+ # If you're not using ActiveRecord, or you'd prefer not to run each of your
24
+ # examples within a transaction, remove the following line or assign false
25
+ # instead of true.
26
+ config.use_transactional_fixtures = true
27
+
28
+ # If true, the base class of anonymous controllers will be inferred
29
+ # automatically. This will be the default behavior in future versions of
30
+ # rspec-rails.
31
+ config.infer_base_class_for_anonymous_controllers = false
32
+ end
@@ -0,0 +1,638 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ require File.dirname(__FILE__) + '/../../../lib/dump_rake'
4
+
5
+ require 'active_record/migration'
6
+
7
+ def object_of_length(required_length)
8
+ LengthConstraint.new(required_length)
9
+ end
10
+
11
+ class LengthConstraint
12
+ def initialize(required_length)
13
+ @required_length = required_length
14
+ end
15
+
16
+ def ==(value)
17
+ @required_length == value.length
18
+ end
19
+ end
20
+
21
+ DumpReader = DumpRake::DumpReader
22
+ describe DumpReader do
23
+ describe "restore" do
24
+ it "should create selves instance and open" do
25
+ @dump = mock('dump')
26
+ @dump.should_receive(:open)
27
+ DumpReader.should_receive(:new).with('/abc/123.tmp').and_return(@dump)
28
+ DumpReader.restore('/abc/123.tmp')
29
+ end
30
+
31
+ it "should call dump subroutines" do
32
+ @dump = mock('dump')
33
+ @dump.stub!(:open).and_yield(@dump)
34
+ DumpReader.stub!(:new).and_return(@dump)
35
+
36
+ @dump.should_receive(:read_config).ordered
37
+ @dump.should_receive(:migrate_down).ordered
38
+ @dump.should_receive(:read_schema).ordered
39
+ @dump.should_receive(:read_tables).ordered
40
+ @dump.should_receive(:read_assets).ordered
41
+
42
+ DumpReader.restore('/abc/123.tmp')
43
+ end
44
+ end
45
+
46
+ describe "summary" do
47
+ Summary = DumpReader::Summary
48
+ describe Summary do
49
+ it "should format text" do
50
+ @summary = Summary.new
51
+ @summary.header 'One'
52
+ @summary.data(%w[fff ggg jjj ppp qqq www])
53
+ @summary.header 'Two'
54
+ @summary.data([['fff', 234], ['ggg', 321], ['jjj', 666], ['ppp', 678], ['qqq', 123], ['www', 345]].map{ |entry| entry.join(': ') })
55
+
56
+ output = <<-TEXT
57
+ One:
58
+ fff
59
+ ggg
60
+ jjj
61
+ ppp
62
+ qqq
63
+ www
64
+ Two:
65
+ fff: 234
66
+ ggg: 321
67
+ jjj: 666
68
+ ppp: 678
69
+ qqq: 123
70
+ www: 345
71
+ TEXT
72
+ "#{@summary}".should == output.gsub(/#{output[/^\s+/]}/, ' ')
73
+ end
74
+
75
+ it "should pluralize" do
76
+ Summary.pluralize(0, 'file').should == '0 files'
77
+ Summary.pluralize(1, 'file').should == '1 file'
78
+ Summary.pluralize(10, 'file').should == '10 files'
79
+ end
80
+ end
81
+
82
+ it "should create selves instance and open" do
83
+ @dump = mock('dump')
84
+ @dump.should_receive(:open)
85
+ DumpReader.should_receive(:new).with('/abc/123.tmp').and_return(@dump)
86
+ DumpReader.summary('/abc/123.tmp')
87
+ end
88
+
89
+ {
90
+ {'path/a' => {:total => 20, :files => 10}, 'path/b' => {:total => 20, :files => 10}} => ['path/a: 10 files (20 entries total)', 'path/b: 10 files (20 entries total)'],
91
+ {'path/a' => 10, 'path/b' => 20} => ['path/a: 10 entries', 'path/b: 20 entries'],
92
+ %w[path/a path/b] => %w[path/a path/b],
93
+ }.each do |assets, formatted_assets|
94
+ it "should call dump subroutines and create summary" do
95
+ tables = {'a' => 10, 'b' => 20, 'c' => 666}
96
+ formatted_tables = ['a: 10 rows', 'b: 20 rows', 'c: 666 rows']
97
+
98
+ @dump = mock('dump')
99
+ @dump.stub!(:config).and_return(:tables => tables, :assets => assets)
100
+ @dump.stub!(:open).and_yield(@dump)
101
+ DumpReader.stub!(:new).and_return(@dump)
102
+ @dump.should_receive(:read_config)
103
+
104
+ @summary = mock('summary')
105
+ @summary.should_receive(:header).with('Tables')
106
+ @summary.should_receive(:data).with(formatted_tables)
107
+ @summary.should_receive(:header).with('Assets')
108
+ @summary.should_receive(:data).with(formatted_assets)
109
+ Summary.stub!(:new).and_return(@summary)
110
+
111
+ DumpReader.summary('/abc/123.tmp').should == @summary
112
+ end
113
+ end
114
+
115
+ it "should call dump subroutines and create summary with schema" do
116
+ tables = {'a' => 10, 'b' => 20, 'c' => 666}
117
+ formatted_tables = ['a: 10 rows', 'b: 20 rows', 'c: 666 rows']
118
+ assets = formatted_assets = %w[path/a path/b]
119
+
120
+ schema = mock('schema')
121
+ schema_lines = mock('schema_lines')
122
+ schema.should_receive(:split).with("\n").and_return(schema_lines)
123
+
124
+ @dump = mock('dump')
125
+ @dump.stub!(:config).and_return(:tables => tables, :assets => assets)
126
+ @dump.stub!(:open).and_yield(@dump)
127
+ @dump.stub!(:schema).and_return(schema)
128
+ DumpReader.stub!(:new).and_return(@dump)
129
+ @dump.should_receive(:read_config)
130
+
131
+ @summary = mock('summary')
132
+ @summary.should_receive(:header).with('Tables')
133
+ @summary.should_receive(:data).with(formatted_tables)
134
+ @summary.should_receive(:header).with('Assets')
135
+ @summary.should_receive(:data).with(formatted_assets)
136
+ @summary.should_receive(:header).with('Schema')
137
+ @summary.should_receive(:data).with(schema_lines)
138
+ Summary.stub!(:new).and_return(@summary)
139
+
140
+ DumpReader.summary('/abc/123.tmp', :schema => true).should == @summary
141
+ end
142
+ end
143
+
144
+ describe "open" do
145
+ it "should set stream to gzipped tar reader" do
146
+ @gzip = mock('gzip')
147
+ @stream = mock('stream')
148
+ Zlib::GzipReader.should_receive(:open).with(Pathname("123.tgz")).and_yield(@gzip)
149
+ Archive::Tar::Minitar::Input.should_receive(:open).with(@gzip).and_yield(@stream)
150
+
151
+ @dump = DumpReader.new('123.tgz')
152
+ @dump.open do |dump|
153
+ dump.should == @dump
154
+ dump.stream.should == @stream
155
+ end
156
+ end
157
+ end
158
+
159
+ describe "low level" do
160
+ before do
161
+ @e1 = mock('e1', :full_name => 'config', :read => 'config_data')
162
+ @e2 = mock('e2', :full_name => 'first.dump', :read => 'first.dump_data')
163
+ @e3 = mock('e3', :full_name => 'second.dump', :read => 'second.dump_data')
164
+ @stream = [@e1, @e2, @e3]
165
+ @dump = DumpReader.new('123.tgz')
166
+ @dump.stub!(:stream).and_return(@stream)
167
+ end
168
+
169
+ describe "find_entry" do
170
+ it "should find first entry in stream equal string" do
171
+ @dump.find_entry('config') do |entry|
172
+ entry.should == @e1
173
+ end
174
+ end
175
+
176
+ it "should find first entry in stream matching regexp" do
177
+ @dump.find_entry(/\.dump$/) do |entry|
178
+ entry.should == @e2
179
+ end
180
+ end
181
+
182
+ it "should return result of block" do
183
+ @dump.find_entry(/\.dump$/) do |entry|
184
+ 'hello'
185
+ end.should == 'hello'
186
+ end
187
+ end
188
+
189
+ describe "read_entry" do
190
+ it "should call find_entry" do
191
+ @dump.should_receive(:find_entry).with('config').and_yield(@e1)
192
+ @dump.read_entry('config')
193
+ end
194
+
195
+ it "should read entries data" do
196
+ @dump.read_entry('config').should == 'config_data'
197
+ end
198
+ end
199
+
200
+ describe "read_entry_to_file" do
201
+ it "should call find_entry" do
202
+ @dump.should_receive(:find_entry).with('config')
203
+ @dump.read_entry_to_file('config')
204
+ end
205
+
206
+ it "should open temp file, write data there, rewind and yield that file" do
207
+ @entry = mock('entry')
208
+ @dump.stub!(:find_entry).and_yield(@entry)
209
+ @temp = mock('temp')
210
+ Tempfile.should_receive(:open).and_yield(@temp)
211
+
212
+ @entry.should_receive(:eof?).and_return(false, false, true)
213
+ @entry.should_receive(:read).with(4096).and_return('a' * 4096, 'b' * 1000)
214
+ @temp.should_receive(:write).with('a' * 4096).ordered
215
+ @temp.should_receive(:write).with('b' * 1000).ordered
216
+ @temp.should_receive(:rewind).ordered
217
+
218
+ @dump.read_entry_to_file('config') do |f|
219
+ f.should == @temp
220
+ end
221
+ end
222
+ end
223
+ end
224
+
225
+ describe "subroutines" do
226
+ before do
227
+ @stream = mock('stream')
228
+ @dump = DumpReader.new('123.tgz')
229
+ @dump.stub!(:stream).and_return(@stream)
230
+ Progress.stub!(:io).and_return(StringIO.new)
231
+ end
232
+
233
+ describe "read_config" do
234
+ it "should read config" do
235
+ @data = {:tables => {:first => 1}, :assets => %w[images videos]}
236
+ @dump.should_receive(:read_entry).with('config').and_return(Marshal.dump(@data))
237
+
238
+ @dump.read_config
239
+ @dump.config.should == @data
240
+ end
241
+ end
242
+
243
+ describe "migrate_down" do
244
+ it "should not invoke rake tasks or find_entry if migrate_down is 0, no or false" do
245
+ Rake::Task.should_not_receive(:[])
246
+ @dump.should_not_receive(:find_entry)
247
+
248
+ DumpRake::Env.with_env(:migrate_down => '0') do
249
+ @dump.migrate_down
250
+ end
251
+ DumpRake::Env.with_env(:migrate_down => 'no') do
252
+ @dump.migrate_down
253
+ end
254
+ DumpRake::Env.with_env(:migrate_down => 'false') do
255
+ @dump.migrate_down
256
+ end
257
+ end
258
+
259
+ it "should invoke db:drop and db:create if migrate_down is reset" do
260
+ @load_task = mock('drop_task')
261
+ @dump_task = mock('create_task')
262
+ Rake::Task.should_receive(:[]).with('db:drop').and_return(@load_task)
263
+ Rake::Task.should_receive(:[]).with('db:create').and_return(@dump_task)
264
+ @load_task.should_receive(:invoke)
265
+ @dump_task.should_receive(:invoke)
266
+
267
+ DumpRake::Env.with_env(:migrate_down => 'reset') do
268
+ @dump.migrate_down
269
+ end
270
+ end
271
+
272
+ [nil, '1'].each do |migrate_down_value|
273
+ describe "when migrate_down is #{migrate_down_value.inspect}" do
274
+ it "should not find_entry if table schema_migrations is not present" do
275
+ @dump.stub!(:avaliable_tables).and_return(%w[first])
276
+ @dump.should_not_receive(:find_entry)
277
+
278
+ DumpRake::Env.with_env(:migrate_down => migrate_down_value) do
279
+ @dump.migrate_down
280
+ end
281
+ end
282
+
283
+ it "should find schema_migrations.dump if table schema_migrations is present" do
284
+ @dump.stub!(:avaliable_tables).and_return(%w[schema_migrations first])
285
+ @dump.should_receive(:find_entry).with('schema_migrations.dump')
286
+
287
+ DumpRake::Env.with_env(:migrate_down => migrate_down_value) do
288
+ @dump.migrate_down
289
+ end
290
+ end
291
+
292
+ it "should call migrate down for each version not present in schema_migrations table" do
293
+ @entry = StringIO.new
294
+ Marshal.dump(['version'], @entry)
295
+ %w[1 2 3 4].each do |i|
296
+ Marshal.dump(i, @entry)
297
+ end
298
+ @entry.rewind
299
+
300
+ @dump.stub!(:avaliable_tables).and_return(%w[schema_migrations first])
301
+ @dump.should_receive(:find_entry).with('schema_migrations.dump').and_yield(@entry)
302
+ @dump.should_receive('table_rows').with('schema_migrations').and_return(%w[1 2 4 5 6 7].map{ |version| {'version' => version} })
303
+
304
+ @versions = []
305
+ @migrate_down_task = mock('migrate_down_task')
306
+ @migrate_down_task.should_receive('invoke').exactly(2).times.with do
307
+ version = DumpRake::Env['VERSION']
308
+ @versions << version
309
+ if version == '6'
310
+ raise ActiveRecord::IrreversibleMigration
311
+ end
312
+ end
313
+ @migrate_down_task.should_receive('reenable').exactly(3).times
314
+
315
+ $stderr.should_receive('puts').with("Irreversible migration: 6")
316
+
317
+ Rake::Task.should_receive(:[]).with('db:migrate:down').exactly(3).times.and_return(@migrate_down_task)
318
+
319
+ DumpRake::Env.with_env(:migrate_down => migrate_down_value) do
320
+ @dump.migrate_down
321
+ end
322
+ @versions.should == %w[5 6 7].reverse
323
+ end
324
+ end
325
+ end
326
+ end
327
+
328
+ describe "read_schema" do
329
+ before do
330
+ @task = mock('task')
331
+ Rake::Task.stub!(:[]).and_return(@task)
332
+ @task.stub!(:invoke)
333
+ end
334
+
335
+ it "should read schema.rb to temp file" do
336
+ @dump.should_receive(:read_entry_to_file).with('schema.rb')
337
+ @dump.read_schema
338
+ end
339
+
340
+ it "should set ENV SCHEMA to temp files path" do
341
+ @file = mock('tempfile', :path => '/temp/123-arst')
342
+ @dump.stub!(:read_entry_to_file).and_yield(@file)
343
+
344
+ DumpRake::Env.should_receive(:with_env).with('SCHEMA' => '/temp/123-arst')
345
+ @dump.read_schema
346
+ end
347
+
348
+ it "should call task db:schema:load and db:schema:dump" do
349
+ @file = mock('tempfile', :path => '/temp/123-arst')
350
+ @dump.stub!(:read_entry_to_file).and_yield(@file)
351
+
352
+ @load_task = mock('load_task')
353
+ @dump_task = mock('dump_task')
354
+ Rake::Task.should_receive(:[]).with('db:schema:load').and_return(@load_task)
355
+ Rake::Task.should_receive(:[]).with('db:schema:dump').and_return(@dump_task)
356
+ @load_task.should_receive(:invoke)
357
+ @dump_task.should_receive(:invoke)
358
+
359
+ @dump.read_schema
360
+ end
361
+ end
362
+
363
+ describe "schema" do
364
+ it "should read schema" do
365
+ @data = %q{create table, rows, etc...}
366
+ @dump.should_receive(:read_entry).with('schema.rb').and_return(@data)
367
+ @dump.schema.should == @data
368
+ end
369
+ end
370
+
371
+ describe "read_tables" do
372
+ it "should verify connection" do
373
+ @dump.stub!(:config).and_return({:tables => []})
374
+ @dump.should_receive(:verify_connection)
375
+ @dump.read_tables
376
+ end
377
+
378
+ it "should call read_table for each table in config" do
379
+ @dump.stub!(:verify_connection)
380
+ @dump.stub!(:config).and_return({:tables => {'first' => 1, 'second' => 3}})
381
+
382
+ @dump.should_receive(:read_table).with('first', 1)
383
+ @dump.should_receive(:read_table).with('second', 3)
384
+
385
+ @dump.read_tables
386
+ end
387
+ end
388
+
389
+ describe "read_table" do
390
+ it "should not read table if no entry found for table" do
391
+ @dump.should_receive(:find_entry).with('first.dump').and_return(nil)
392
+ @dump.should_not_receive(:quote_table_name)
393
+ @dump.read_table('first', 10)
394
+ end
395
+
396
+ it "should clear table and read table if entry found for table" do
397
+ @entry = mock('entry', :to_str => Marshal.dump('data'), :eof? => true)
398
+ @dump.should_receive(:columns_insert_sql).with('data')
399
+ @dump.should_receive(:find_entry).with('first.dump').and_yield(@entry)
400
+ @dump.should_receive(:quote_table_name).with('first').and_return('`first`')
401
+ @dump.should_receive(:clear_table).with('`first`')
402
+ @dump.read_table('first', 10)
403
+ end
404
+
405
+ it "should clear schema table before writing" do
406
+ @entry = mock('entry', :to_str => Marshal.dump('data'), :eof? => true)
407
+ @dump.should_receive(:columns_insert_sql).with('data')
408
+ @dump.should_receive(:find_entry).with('schema_migrations.dump').and_yield(@entry)
409
+ @dump.should_receive(:quote_table_name).with('schema_migrations').and_return('`schema_migrations`')
410
+ @dump.should_receive(:clear_table).with('`schema_migrations`')
411
+ @dump.read_table('schema_migrations', 10)
412
+ end
413
+
414
+ describe "reading/writing data" do
415
+ def create_entry(rows_count)
416
+ @entry = StringIO.new
417
+
418
+ @columns = %w[id name]
419
+ @rows = []
420
+ Marshal.dump(@columns, @entry)
421
+ (1..rows_count).each do |i|
422
+ row = [i, "entry#{i}"]
423
+ @rows << row
424
+ Marshal.dump(row, @entry)
425
+ end
426
+ @entry.rewind
427
+
428
+ @dump.stub!(:find_entry).and_yield(@entry)
429
+ end
430
+ it "should read to eof" do
431
+ create_entry(2500)
432
+ @dump.stub!(:clear_table)
433
+ @dump.stub!(:insert_into_table)
434
+ @dump.read_table('first', 2500)
435
+ @entry.eof?.should be_true
436
+ end
437
+
438
+ it "should try to insert rows in slices of 1000 rows" do
439
+ create_entry(2500)
440
+ @dump.stub!(:clear_table)
441
+ @dump.should_receive(:insert_into_table).with(anything, anything, object_of_length(1000)).twice
442
+ @dump.should_receive(:insert_into_table).with(anything, anything, object_of_length(500)).once
443
+
444
+ @dump.read_table('first', 2500)
445
+ end
446
+
447
+ it "should try to insert row by row if slice method fails" do
448
+ create_entry(2500)
449
+ @dump.stub!(:clear_table)
450
+ @dump.should_receive(:insert_into_table).with(anything, anything, kind_of(Array)).exactly(3).times.and_raise('sql error')
451
+ @dump.should_receive(:insert_into_table).with(anything, anything, kind_of(String)).exactly(2500).times
452
+ @dump.read_table('first', 2500)
453
+ end
454
+
455
+ it "should quote table, columns and values and send them to insert_into_table" do
456
+ create_entry(100)
457
+ @dump.stub!(:clear_table)
458
+ @dump.should_receive(:quote_table_name).with('first').and_return('`first`')
459
+ @dump.should_receive(:columns_insert_sql).with(@columns).and_return('(`id`, `name`)')
460
+ @rows.each do |row|
461
+ @dump.should_receive(:values_insert_sql).with(row).and_return{ |vs| vs.inspect }
462
+ end
463
+
464
+ @dump.should_receive(:insert_into_table).with('`first`', '(`id`, `name`)', @rows.map(&:inspect))
465
+ @dump.read_table('first', 100)
466
+ end
467
+ end
468
+ end
469
+
470
+ describe "read_assets" do
471
+ before do
472
+ @task = mock('task')
473
+ Rake::Task.stub!(:[]).with('assets:delete').and_return(@task)
474
+ @task.stub!(:invoke)
475
+ @dump.stub!(:assets_root_link).and_yield('/tmp', 'assets')
476
+ end
477
+
478
+ it "should not read assets if config[:assets] is nil" do
479
+ @dump.stub!(:config).and_return({})
480
+ @dump.should_not_receive(:find_entry)
481
+ @dump.read_assets
482
+ end
483
+
484
+ it "should not read assets if config[:assets] is blank" do
485
+ @dump.stub!(:config).and_return({:assets => []})
486
+ @dump.should_not_receive(:find_entry)
487
+ @dump.read_assets
488
+ end
489
+
490
+ describe "deleting existing assets" do
491
+ before do
492
+ @stream.stub!(:each)
493
+ end
494
+
495
+ it "should call assets:delete" do
496
+ @assets = %w[images videos]
497
+ @dump.stub!(:config).and_return({:assets => @assets})
498
+ @dump.stub!(:find_entry)
499
+
500
+ @task.should_receive(:invoke)
501
+
502
+ @dump.read_assets
503
+ end
504
+
505
+ it "should call assets:delete with ASSETS set to config[:assets] joined with :" do
506
+ @assets = %w[images videos]
507
+ @dump.stub!(:config).and_return({:assets => @assets})
508
+ @dump.stub!(:find_entry)
509
+
510
+ def @task.invoke
511
+ DumpRake::Env[:assets].should == 'images:videos'
512
+ end
513
+
514
+ @dump.read_assets
515
+ end
516
+
517
+ describe "when called with restore_assets" do
518
+ it "should delete files and dirs only in requested paths" do
519
+ @assets = %w[images videos]
520
+ @dump.stub!(:config).and_return({:assets => @assets})
521
+
522
+ DumpRake::Assets.should_receive('glob_asset_children').with('images', '**/*').and_return(%w[images images/a.jpg images/b.jpg])
523
+ DumpRake::Assets.should_receive('glob_asset_children').with('videos', '**/*').and_return(%w[videos videos/a.mov])
524
+
525
+ @dump.should_receive('read_asset?').with('images/b.jpg', DumpRake::RailsRoot).ordered.and_return(false)
526
+ @dump.should_receive('read_asset?').with('images/a.jpg', DumpRake::RailsRoot).ordered.and_return(true)
527
+ @dump.should_receive('read_asset?').with('images', DumpRake::RailsRoot).ordered.and_return(true)
528
+ @dump.should_receive('read_asset?').with('videos/a.mov', DumpRake::RailsRoot).ordered.and_return(false)
529
+ @dump.should_receive('read_asset?').with('videos', DumpRake::RailsRoot).ordered.and_return(false)
530
+
531
+ File.should_receive('file?').with('images/a.jpg').and_return(true)
532
+ File.should_receive('unlink').with('images/a.jpg')
533
+ File.should_not_receive('file?').with('images/b.jpg')
534
+ File.should_receive('file?').with('images').and_return(false)
535
+ File.should_receive('directory?').with('images').and_return(true)
536
+ Dir.should_receive('unlink').with('images').and_raise(Errno::ENOTEMPTY)
537
+
538
+ DumpRake::Env.with_env(:restore_assets => 'images/a.*:stylesheets') do
539
+ @dump.read_assets
540
+ end
541
+ end
542
+ end
543
+ end
544
+
545
+ describe "old style" do
546
+ it "should find assets.tar" do
547
+ @assets = %w[images videos]
548
+ @dump.stub!(:config).and_return({:assets => @assets})
549
+ Dir.stub!(:glob).and_return([])
550
+ FileUtils.stub!(:remove_entry)
551
+ @stream.stub!(:each)
552
+
553
+ @dump.should_receive(:find_entry).with('assets.tar')
554
+ @dump.read_assets
555
+ end
556
+
557
+ [
558
+ %w[images videos],
559
+ {'images' => 0, 'videos' => 0},
560
+ {'images' => {:files => 0, :total => 0}, 'videos' => {:files => 0, :total => 0}},
561
+ ].each do |assets|
562
+ it "should rewrite rewind method to empty method - to not raise exception, open tar and extract each entry" do
563
+ @dump.stub!(:config).and_return({:assets => assets})
564
+ Dir.stub!(:glob).and_return([])
565
+ FileUtils.stub!(:remove_entry)
566
+
567
+ @assets_tar = mock('assets_tar')
568
+ @assets_tar.stub!(:rewind).and_raise('hehe - we want to rewind to center of gzip')
569
+ @dump.stub!(:find_entry).and_yield(@assets_tar)
570
+
571
+ @inp = mock('inp')
572
+ each_excpectation = @inp.should_receive(:each)
573
+ @entries = %w[a b c d].map do |s|
574
+ file = mock("file_#{s}")
575
+ each_excpectation.and_yield(file)
576
+ @inp.should_receive(:extract_entry).with(DumpRake::RailsRoot, file)
577
+ file
578
+ end
579
+ Archive::Tar::Minitar.should_receive(:open).with(@assets_tar).and_yield(@inp)
580
+
581
+ @dump.read_assets
582
+ end
583
+ end
584
+ end
585
+
586
+ describe "new style" do
587
+ before do
588
+ @dump.should_receive(:find_entry).with('assets.tar')
589
+ end
590
+
591
+ [
592
+ %w[images videos],
593
+ {'images' => 0, 'videos' => 0},
594
+ {'images' => {:files => 0, :total => 0}, 'videos' => {:files => 0, :total => 0}},
595
+ ].each do |assets|
596
+ it "should extract each entry" do
597
+ @dump.stub!(:config).and_return({:assets => assets})
598
+ Dir.stub!(:glob).and_return([])
599
+ FileUtils.stub!(:remove_entry)
600
+
601
+ @dump.should_receive(:assets_root_link).and_yield('/tmp/abc', 'assets')
602
+ each_excpectation = @stream.should_receive(:each)
603
+ @entries = %w[a b c d].map do |s|
604
+ file = mock("file_#{s}", :full_name => "assets/#{s}")
605
+ each_excpectation.and_yield(file)
606
+ @stream.should_receive(:extract_entry).with('/tmp/abc', file)
607
+ file
608
+ end
609
+ other_file = mock('other_file', :full_name => 'other_file')
610
+ each_excpectation.and_yield(other_file)
611
+ @stream.should_not_receive(:extract_entry).with('/tmp/abc', other_file)
612
+
613
+ @dump.read_assets
614
+ end
615
+ end
616
+ end
617
+ end
618
+
619
+ describe "read_asset?" do
620
+ it "should create filter and call custom_pass? on it" do
621
+ @filter = mock('filter')
622
+ @filter.stub!('custom_pass?')
623
+
624
+ DumpRake::Env.should_receive('filter').with(:restore_assets, DumpRake::Assets::SPLITTER).and_return(@filter)
625
+
626
+ @dump.read_asset?('a', 'b')
627
+ end
628
+
629
+ it "should test path usint fnmatch" do
630
+ DumpRake::Env.with_env(:restore_assets => '[a-b]') do
631
+ @dump.read_asset?('x/a', 'x').should be_true
632
+ @dump.read_asset?('x/b/file', 'x').should be_true
633
+ @dump.read_asset?('x/c', 'x').should be_false
634
+ end
635
+ end
636
+ end
637
+ end
638
+ end