my_enginery 0.2.8
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +18 -0
- data/.travis.yml +9 -0
- data/CHANGELOG.md +14 -0
- data/Gemfile +12 -0
- data/LICENSE +19 -0
- data/README.md +957 -0
- data/Rakefile +48 -0
- data/app/base/.pryrc +1 -0
- data/app/base/Gemfile +25 -0
- data/app/base/Rakefile +4 -0
- data/app/base/app.rb +8 -0
- data/app/base/base/boot.rb +45 -0
- data/app/base/base/config.rb +127 -0
- data/app/base/base/controllers/rear-controllers/.gitkeep +0 -0
- data/app/base/base/database.rb +3 -0
- data/app/base/base/helpers/application_helpers.rb +3 -0
- data/app/base/base/migrations/.gitkeep +0 -0
- data/app/base/base/models/.gitkeep +0 -0
- data/app/base/base/specs/.gitkeep +0 -0
- data/app/base/base/views/.gitkeep +0 -0
- data/app/base/config.ru +4 -0
- data/app/base/config/config.yml +17 -0
- data/app/base/public/assets/Enginery.png +0 -0
- data/app/base/public/assets/Espresso.png +0 -0
- data/app/base/public/assets/application.css +13 -0
- data/app/base/public/assets/application.js +2 -0
- data/app/base/public/assets/bootstrap/css/bootstrap-responsive.min.css +9 -0
- data/app/base/public/assets/bootstrap/css/bootstrap.min.css +9 -0
- data/app/base/public/assets/bootstrap/img/glyphicons-halflings-white.png +0 -0
- data/app/base/public/assets/bootstrap/img/glyphicons-halflings.png +0 -0
- data/app/base/public/assets/bootstrap/js/bootstrap.min.js +6 -0
- data/app/base/public/assets/jquery.js +6 -0
- data/app/base/var/db/.gitkeep +0 -0
- data/app/base/var/log/.gitkeep +0 -0
- data/app/base/var/pid/.gitkeep +0 -0
- data/app/database/ActiveRecord.rb +11 -0
- data/app/database/DataMapper.rb +11 -0
- data/app/database/Sequel.rb +11 -0
- data/app/database/mysql.yml +24 -0
- data/app/database/postgres.yml +24 -0
- data/app/database/sqlite.yml +24 -0
- data/app/gemfiles/ActiveRecord.rb +1 -0
- data/app/gemfiles/BlueCloth.rb +1 -0
- data/app/gemfiles/DataMapper.rb +1 -0
- data/app/gemfiles/FastCGI.rb +1 -0
- data/app/gemfiles/Puma.rb +1 -0
- data/app/gemfiles/RDiscount.rb +1 -0
- data/app/gemfiles/RDoc.rb +1 -0
- data/app/gemfiles/RedCloth.rb +1 -0
- data/app/gemfiles/WEBrick.rb +1 -0
- data/app/gemfiles/WikiCloth.rb +1 -0
- data/app/gemfiles/mysql/ActiveRecord.rb +1 -0
- data/app/gemfiles/mysql/DataMapper.rb +1 -0
- data/app/gemfiles/mysql/Sequel.rb +1 -0
- data/app/gemfiles/postgres/ActiveRecord.rb +1 -0
- data/app/gemfiles/postgres/DataMapper.rb +1 -0
- data/app/gemfiles/postgres/Sequel.rb +1 -0
- data/app/gemfiles/sqlite/ActiveRecord.rb +1 -0
- data/app/gemfiles/sqlite/DataMapper.rb +1 -0
- data/app/gemfiles/sqlite/Sequel.rb +1 -0
- data/app/layouts/ERB/layout.erb +56 -0
- data/app/layouts/Erubis/layout.erb +56 -0
- data/app/layouts/Haml/layout.haml +38 -0
- data/app/layouts/Slim/layout.slim +38 -0
- data/app/migrations/ActiveRecord.erb +51 -0
- data/app/migrations/DataMapper.erb +61 -0
- data/app/migrations/Sequel.erb +54 -0
- data/app/migrations/tracking_table/ActiveRecord.rb +19 -0
- data/app/migrations/tracking_table/DataMapper.rb +26 -0
- data/app/migrations/tracking_table/Sequel.rb +18 -0
- data/app/rakefiles/ActiveRecord.rb +0 -0
- data/app/rakefiles/DataMapper.rb +1 -0
- data/app/rakefiles/Sequel.rb +0 -0
- data/app/rakefiles/Specular.rb +1 -0
- data/app/specfiles/Specular.erb +7 -0
- data/bin/my_enginery +235 -0
- data/lib/enginery.rb +23 -0
- data/lib/enginery/cli.rb +44 -0
- data/lib/enginery/configurator.rb +139 -0
- data/lib/enginery/delete.rb +116 -0
- data/lib/enginery/enginery.rb +41 -0
- data/lib/enginery/generator.rb +325 -0
- data/lib/enginery/helpers/app.rb +83 -0
- data/lib/enginery/helpers/generic.rb +145 -0
- data/lib/enginery/helpers/input.rb +123 -0
- data/lib/enginery/helpers/orm.rb +86 -0
- data/lib/enginery/helpers/validations.rb +101 -0
- data/lib/enginery/migrator.rb +371 -0
- data/lib/enginery/rake-tasks/data_mapper.rb +49 -0
- data/lib/enginery/rake-tasks/specular.rb +41 -0
- data/lib/enginery/registry.rb +101 -0
- data/lib/enginery/usage.rb +66 -0
- data/lib/enginery/version.rb +7 -0
- data/logo.png +0 -0
- data/my_enginery.gemspec +37 -0
- data/test/delete/test__admin.rb +49 -0
- data/test/delete/test__controller.rb +37 -0
- data/test/delete/test__helper.rb +49 -0
- data/test/delete/test__migration.rb +27 -0
- data/test/delete/test__model.rb +35 -0
- data/test/delete/test__route.rb +35 -0
- data/test/delete/test__spec.rb +59 -0
- data/test/delete/test__view.rb +51 -0
- data/test/generator/test__admin.rb +39 -0
- data/test/generator/test__controller.rb +123 -0
- data/test/generator/test__helper.rb +56 -0
- data/test/generator/test__model.rb +206 -0
- data/test/generator/test__project.rb +81 -0
- data/test/generator/test__route.rb +110 -0
- data/test/generator/test__spec.rb +56 -0
- data/test/generator/test__view.rb +85 -0
- data/test/migrator/test__auto_generation.rb +41 -0
- data/test/migrator/test__manual_generation.rb +59 -0
- data/test/migrator/test__migrations.rb +139 -0
- data/test/sandbox/.gitkeep +0 -0
- data/test/setup.rb +29 -0
- data/test/support/spec_helpers.rb +151 -0
- metadata +392 -0
@@ -0,0 +1,371 @@
|
|
1
|
+
module Enginery
|
2
|
+
class Migrator
|
3
|
+
include Helpers
|
4
|
+
|
5
|
+
TIME_FORMAT = '%Y-%m-%d_%H-%M-%S'.freeze
|
6
|
+
NAME_REGEXP = /\A(\d+)\.(\d+\-\d+\-\d+_\d+\-\d+\-\d+)\.(.*)#{Regexp.escape MIGRATION_SUFFIX}\Z/.freeze
|
7
|
+
|
8
|
+
def initialize dst_root, setups = {}
|
9
|
+
@dst_root, @setups = dst_root, setups
|
10
|
+
@migrations = Dir[dst_path(:migrations, '**/*%s' % MIGRATION_SUFFIX)].inject([]) do |map,f|
|
11
|
+
step, time, name = File.basename(f).scan(NAME_REGEXP).flatten
|
12
|
+
step && time && name && map << [step.to_i, time, name, f.sub(dst_path.migrations, '')]
|
13
|
+
map
|
14
|
+
end.sort {|a,b| a.first <=> b.first}.freeze
|
15
|
+
end
|
16
|
+
|
17
|
+
# generate new migration.
|
18
|
+
# it will create a [n].[timestamp].[name].rb migration file in base/migrations/
|
19
|
+
# and column_transitions.yml file in base/migrations/track/
|
20
|
+
# migration file will contain "up" and "down" sections.
|
21
|
+
# column_transitions file will keep track of column type changes.
|
22
|
+
#
|
23
|
+
def new name
|
24
|
+
(name.nil? || name.empty?) && fail("Please provide migration name via second argument")
|
25
|
+
(name =~ /[^\w|\d|\-|\.|\:]/) && fail("Migration name can contain only alphanumerics, dashes, semicolons and dots")
|
26
|
+
@migrations.any? {|m| m[2] == name} && fail('"%s" migration already exists' % name)
|
27
|
+
|
28
|
+
max = (@migrations.max {|m| m.first}||[0]).first
|
29
|
+
model = @setups[:create_table] || @setups[:update_table]
|
30
|
+
context = {model: model, name: name, step: max + 1}
|
31
|
+
|
32
|
+
[:create_table, :update_table].each do |o|
|
33
|
+
context[o] = (m = constant_defined?(@setups[o])) ? model_to_table(m) : nil
|
34
|
+
end
|
35
|
+
table = context[:create_table] || context[:update_table] ||
|
36
|
+
fail('No model provided or provided one does not exists!')
|
37
|
+
|
38
|
+
[:create_columns, :update_columns].each do |o|
|
39
|
+
context[o] = transitions(table, (@setups[o]||[]).map {|(n,t)| [n, opted_column_type(t)]})
|
40
|
+
end
|
41
|
+
context[:rename_columns] = @setups[:rename_columns]||[]
|
42
|
+
|
43
|
+
engine = Tenjin::Engine.new(path: [src_path.migrations], cache: false)
|
44
|
+
source_code = engine.render('%s.erb' % guess_orm, context.merge(context: context))
|
45
|
+
|
46
|
+
o
|
47
|
+
o '--- %s model - generating "%s" migration ---' % [model, name]
|
48
|
+
o
|
49
|
+
o ' Serial Number: %s' % context[:step]
|
50
|
+
o
|
51
|
+
time = Time.now.strftime(TIME_FORMAT)
|
52
|
+
path = dst_path(:migrations, class_to_route(model))
|
53
|
+
FileUtils.mkdir_p(path)
|
54
|
+
file = File.join(path, [context[:step], time, name, 'rb']*'.')
|
55
|
+
write_file file, source_code
|
56
|
+
output_source_code source_code.split("\n")
|
57
|
+
name
|
58
|
+
end
|
59
|
+
|
60
|
+
# convert given range or a single migration into files to be run
|
61
|
+
# ex: 1-5 will run migrations from one to 5 inclusive
|
62
|
+
# 1 2 4 will run 1st, 2nd, and 4th migrations
|
63
|
+
# 2 will run only 2nd migration
|
64
|
+
def serials_to_files vector, *serials
|
65
|
+
vector = validate_vector(vector)
|
66
|
+
serials.map do |serial|
|
67
|
+
if serial =~ /\-/
|
68
|
+
a, z = serial.split('-')
|
69
|
+
(a..z).to_a
|
70
|
+
else
|
71
|
+
serial
|
72
|
+
end
|
73
|
+
end.flatten.map do |e|
|
74
|
+
@migrations.find {|m| m.first == e.to_i} ||
|
75
|
+
fail('Wrong range provided. "%s" is not a recognized migration step' % e)
|
76
|
+
end.sort do |a,b|
|
77
|
+
vector == :up ? a.first <=> b.first : b.first <=> a.first
|
78
|
+
end.map(&:last)
|
79
|
+
end
|
80
|
+
|
81
|
+
# - validate migration file name
|
82
|
+
# - apply migration in given direction if migration was not previously performed
|
83
|
+
# in given direction or :force option given
|
84
|
+
# - create a track in TRACKING_TABLE
|
85
|
+
# so on consequent requests we may know whether migration was already performed
|
86
|
+
def run vector, file, force_run = nil
|
87
|
+
vector = validate_vector(vector)
|
88
|
+
|
89
|
+
(migration = @migrations.find {|m| m.last == file}) ||
|
90
|
+
fail('"%s" is not a valid migration file' % file)
|
91
|
+
|
92
|
+
create_tracking_table_if_needed
|
93
|
+
|
94
|
+
track = track_exists?(file, vector)
|
95
|
+
if track && !force_run
|
96
|
+
o
|
97
|
+
o '*** Skipping "%s: %s" migration ***' % [migration[0], migration[2]]
|
98
|
+
o ' It was already performed %s on %s' % [track.vector.upcase, track.performed_at]
|
99
|
+
o ' Use :force option to run it anyway - enginery m:%s:force ...' % vector
|
100
|
+
o
|
101
|
+
return
|
102
|
+
end
|
103
|
+
apply!(migration, vector) && persist_track(file, vector)
|
104
|
+
end
|
105
|
+
|
106
|
+
# list available migrations with date of last run, if any
|
107
|
+
def list
|
108
|
+
create_tracking_table_if_needed
|
109
|
+
o indent('--'), '-=---'
|
110
|
+
@migrations.each do |(step,time,name,file)|
|
111
|
+
track = track_exists?(file)
|
112
|
+
last_perform = track ? '%s on %s' % [track.vector, track.performed_at] : 'none'
|
113
|
+
o indent(step), ' : ', name
|
114
|
+
o indent('created at'), ' : ', DateTime.strptime(time, TIME_FORMAT).rfc2822
|
115
|
+
o indent('last performed'), ' : ', last_perform
|
116
|
+
o indent('--'), '-=---'
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def outstanding_migrations vector
|
121
|
+
create_tracking_table_if_needed
|
122
|
+
serials = @migrations.inject([]) do |l,(step,time,name,file)|
|
123
|
+
track_exists?(File.basename(file), vector) ? l : l.push(step)
|
124
|
+
end
|
125
|
+
serials_to_files(vector, *serials)
|
126
|
+
end
|
127
|
+
|
128
|
+
def last_run file
|
129
|
+
create_tracking_table_if_needed
|
130
|
+
return unless track = track_exists?(file)
|
131
|
+
[track.vector, track.performed_at]
|
132
|
+
end
|
133
|
+
|
134
|
+
def update_model_file context, vector
|
135
|
+
model = context[:model]
|
136
|
+
file = dst_path(:models, class_to_route(model) + MODEL_SUFFIX)
|
137
|
+
return unless File.file?(file)
|
138
|
+
|
139
|
+
lines, properties = File.readlines(file), []
|
140
|
+
lines.each_with_index do |l,i|
|
141
|
+
property = l.scan(/(\s+)?property\s+[\W]?(\w+)\W+(\w+)(.*)/).flatten
|
142
|
+
properties << (property << i) if property[1] && property[2]
|
143
|
+
end
|
144
|
+
return if properties.empty?
|
145
|
+
|
146
|
+
new_lines = case vector.to_s.downcase.to_sym
|
147
|
+
when :up
|
148
|
+
add_properties(lines, properties, context)
|
149
|
+
when :down
|
150
|
+
remove_properties(lines, properties, context)
|
151
|
+
end
|
152
|
+
|
153
|
+
return unless new_lines
|
154
|
+
File.open(file, 'w') {|f| f << new_lines.join}
|
155
|
+
end
|
156
|
+
|
157
|
+
def guess_orm
|
158
|
+
orm = (@setups[:orm] || Cfg[:orm] || fail('No project-wide ORM detected.
|
159
|
+
Please update config/config.yml by adding
|
160
|
+
orm: [DataMapper|ActiveRecord|Sequel]')).to_s.strip
|
161
|
+
(ORM_MATCHERS.find {|o,m| orm =~ m} || fail('"%s" ORM not supported')).first
|
162
|
+
end
|
163
|
+
|
164
|
+
private
|
165
|
+
|
166
|
+
# load migration file and call corresponding methods that will run migration up/down
|
167
|
+
def apply! migration, vector, orm = guess_orm
|
168
|
+
o
|
169
|
+
o '*** Performing %s step #%s ***' % [vector, migration.first]
|
170
|
+
o ' Label: %s' % migration[2]
|
171
|
+
o ' ORM: %s' % orm
|
172
|
+
begin
|
173
|
+
|
174
|
+
load dst_path(:migrations, migration.last)
|
175
|
+
|
176
|
+
case orm
|
177
|
+
when :DataMapper
|
178
|
+
|
179
|
+
update_model_file(MigratorContext, vector)
|
180
|
+
|
181
|
+
mj, mn, pt = DataMapper::VERSION.scan(/\d+/).map(&:to_i)
|
182
|
+
if MigratorContext[:rename_columns].any? && [1,2,0] == [mj,mn,pt]
|
183
|
+
o ' status: Skipped as renaming columns is broken on DataMapper 1.2.0'
|
184
|
+
return false
|
185
|
+
end
|
186
|
+
|
187
|
+
MigratorInstance.instance_exec do
|
188
|
+
# when using perform_up/down DataMapper will create a tracking table
|
189
|
+
# and decide whether migration should be run, based on needs_up? and needs_down?
|
190
|
+
# Enginery keeps own tracks and does not need DataMapper's tracking table
|
191
|
+
# nor decisions on running migrations,
|
192
|
+
# so using instance_exec to apply migrations directly.
|
193
|
+
if action = instance_variable_get('@%s_action' % vector)
|
194
|
+
action.call
|
195
|
+
end
|
196
|
+
end
|
197
|
+
when :ActiveRecord
|
198
|
+
MigratorInstance.new.send vector
|
199
|
+
when :Sequel
|
200
|
+
model = constant_defined?(MigratorContext[:model])
|
201
|
+
MigratorInstance.apply model.db, vector
|
202
|
+
end
|
203
|
+
o ' status: OK'
|
204
|
+
true
|
205
|
+
rescue => e
|
206
|
+
fail e.message, *e.backtrace
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
def add_properties lines, properties, context
|
211
|
+
property_setup, new_properties = nil, []
|
212
|
+
|
213
|
+
context[:create_columns].each do |(n,t)|
|
214
|
+
next if properties.find {|p| p[1].to_s == n.to_s}
|
215
|
+
property_setup = [properties.last.first, n, t.to_s.split('::').last]
|
216
|
+
new_properties << '%sproperty :%s, %s' % property_setup
|
217
|
+
end
|
218
|
+
if new_properties.any?
|
219
|
+
lines[properties.last.last] += (new_properties.join("\n") + "\n")
|
220
|
+
end
|
221
|
+
|
222
|
+
context[:rename_columns].each do |(cn,nn)|
|
223
|
+
next unless property = properties.find {|p| p[1].to_s == cn.to_s}
|
224
|
+
property_setup = [property[0], nn, *property[2..3]]
|
225
|
+
lines[property.last] = "%sproperty :%s, %s%s\n" % property_setup
|
226
|
+
end
|
227
|
+
|
228
|
+
context[:update_columns].each do |(n,t)|
|
229
|
+
next unless property = properties.find {|p| p[1].to_s == n.to_s}
|
230
|
+
property_setup = [*property[0..1], t.to_s.split('::').last, property[3]]
|
231
|
+
lines[property.last] = "%sproperty :%s, %s%s\n" % property_setup
|
232
|
+
end
|
233
|
+
|
234
|
+
property_setup ? lines : nil
|
235
|
+
end
|
236
|
+
|
237
|
+
|
238
|
+
def remove_properties lines, properties, context
|
239
|
+
property = nil
|
240
|
+
|
241
|
+
context[:create_columns].each do |(n)|
|
242
|
+
next unless property = properties.find {|p| p[1].to_s == n.to_s}
|
243
|
+
lines[property.last] = nil
|
244
|
+
end
|
245
|
+
|
246
|
+
context[:rename_columns].each do |(cn,nn)|
|
247
|
+
next unless property = properties.find {|p| p[1].to_s == nn.to_s}
|
248
|
+
property[1] = cn
|
249
|
+
lines[property.last] = "%sproperty :%s, %s%s\n" % property
|
250
|
+
end
|
251
|
+
|
252
|
+
context[:update_columns].each do |(n,nt,ot)|
|
253
|
+
next unless property = properties.find {|p| p[1].to_s == n.to_s}
|
254
|
+
property[2] = ot.to_s.split('::').last
|
255
|
+
lines[property.last] = "%sproperty :%s, %s%s\n" % property
|
256
|
+
end
|
257
|
+
|
258
|
+
property ? lines : nil
|
259
|
+
end
|
260
|
+
|
261
|
+
def create_tracking_table_if_needed
|
262
|
+
require src_path(:migrations, 'tracking_table/%s.rb' % guess_orm)
|
263
|
+
case guess_orm
|
264
|
+
when :DataMapper
|
265
|
+
TracksMigrator.instance_exec { @up_action.call }
|
266
|
+
when :ActiveRecord
|
267
|
+
TracksMigrator.new.up
|
268
|
+
when :Sequel
|
269
|
+
TracksMigrator.apply Sequel::Model.db, :up
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
def track_exists? migration, vector = nil
|
274
|
+
conditions = {migration: migration}
|
275
|
+
conditions[:vector] = vector.to_s if vector # #to_s required on Sequel
|
276
|
+
case guess_orm
|
277
|
+
when :ActiveRecord, :DataMapper
|
278
|
+
TracksModel.first(conditions: conditions)
|
279
|
+
when :Sequel
|
280
|
+
TracksModel.first(conditions)
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
def persist_track migration, vector
|
285
|
+
key = {migration: migration}
|
286
|
+
row = key.merge(performed_at: DateTime.now.rfc2822, vector: vector.to_s)
|
287
|
+
case guess_orm
|
288
|
+
when :DataMapper
|
289
|
+
TracksModel.all(key).destroy!
|
290
|
+
TracksModel.create(row)
|
291
|
+
when :ActiveRecord
|
292
|
+
TracksModel.delete_all(key)
|
293
|
+
TracksModel.create(row)
|
294
|
+
when :Sequel
|
295
|
+
TracksModel.where(key).delete
|
296
|
+
TracksModel.insert(row)
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
# get the actual db table of a given model
|
301
|
+
def model_to_table model
|
302
|
+
case guess_orm
|
303
|
+
when :DataMapper
|
304
|
+
model.repository.adapter.resource_naming_convention.call(model)
|
305
|
+
when :ActiveRecord, :Sequel
|
306
|
+
model.table_name
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
def default_column_type orm = guess_orm
|
311
|
+
case orm
|
312
|
+
when :ActiveRecord
|
313
|
+
'string'
|
314
|
+
when :DataMapper, :Sequel
|
315
|
+
'String'
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
# convert given string into column type suitable for migration file
|
320
|
+
def opted_column_type type, orm = nil
|
321
|
+
orm ||= guess_orm
|
322
|
+
type ||= default_column_type(orm)
|
323
|
+
case orm
|
324
|
+
when :DataMapper
|
325
|
+
'DataMapper::Property::%s' % capitalize(type)
|
326
|
+
when :Sequel
|
327
|
+
type.to_s =~ /text/i ? "String, text: true" : capitalize(type)
|
328
|
+
else
|
329
|
+
type
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
# someString.capitalize will return Somestring.
|
334
|
+
# we need SomeString instead, which is returned by this method
|
335
|
+
def capitalize smth
|
336
|
+
smth.to_s.match(/(\w)(.*)/) {|m| m[1].upcase << m[2]}
|
337
|
+
end
|
338
|
+
|
339
|
+
def validate_vector vector
|
340
|
+
invalid_vector!(vector) unless vector.is_a?(String)
|
341
|
+
(vector =~ /\Au/i) && (vector = :up)
|
342
|
+
(vector =~ /\Ad/i) && (vector = :down)
|
343
|
+
invalid_vector!(vector) unless vector.is_a?(Symbol)
|
344
|
+
vector
|
345
|
+
end
|
346
|
+
|
347
|
+
def invalid_vector! vector
|
348
|
+
fail('%s is a unrecognized vector. Use either "up" or "down"' % vector.inspect)
|
349
|
+
end
|
350
|
+
|
351
|
+
def indent smth
|
352
|
+
string = smth.to_s
|
353
|
+
ident_size = 20 - string.size
|
354
|
+
ident_size = 0 if ident_size < 0
|
355
|
+
INDENT + ' '*ident_size + string
|
356
|
+
end
|
357
|
+
|
358
|
+
def transitions table, columns
|
359
|
+
transitions_file = dst_path(:migrations, 'transitions.yml')
|
360
|
+
transitions = File.file?(transitions_file) ? (YAML.load(File.read(transitions_file)) rescue {}) : {}
|
361
|
+
transitions[table] ||= {}
|
362
|
+
columns.each do |column|
|
363
|
+
column[2] = transitions[table][column.first]
|
364
|
+
transitions[table][column.first] = column[1]
|
365
|
+
end
|
366
|
+
File.open(transitions_file, 'w') {|f| f << YAML.dump(transitions)}
|
367
|
+
columns
|
368
|
+
end
|
369
|
+
|
370
|
+
end
|
371
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
|
2
|
+
namespace :dm do
|
3
|
+
%w[auto_migrate auto_upgrade].each do |t|
|
4
|
+
alert = t == 'auto_migrate' ? 'ACHTUNG! This is a DESTRUCTIVE action!' : nil
|
5
|
+
|
6
|
+
ObjectSpace.each_object(Class).select do |c|
|
7
|
+
c.ancestors.include?(DataMapper::Resource)
|
8
|
+
end.each do |m|
|
9
|
+
run = lambda do
|
10
|
+
puts '', ' Running %s.%s!' % [m.name, t], ''
|
11
|
+
m.send(t + '!')
|
12
|
+
end
|
13
|
+
|
14
|
+
desc 'Run %s.%s! %s' % [m.name, t, alert]
|
15
|
+
task [t, m.name]*':' do
|
16
|
+
if alert
|
17
|
+
puts '', alert
|
18
|
+
puts ' The table for %s model will be destroyed and recreated from ground up.' % m.name
|
19
|
+
puts ' Any data will be lost, so please consider to take some backups before continuing.', ''
|
20
|
+
puts ' Type Y and press enter to continue'
|
21
|
+
puts ' Press enter to cancel'
|
22
|
+
answer = STDIN.gets.strip
|
23
|
+
(puts 'exiting...'; exit(0)) unless answer == 'Y'
|
24
|
+
end
|
25
|
+
run.call
|
26
|
+
end
|
27
|
+
task([t, m.name ,'y']*':') { run.call }
|
28
|
+
end
|
29
|
+
|
30
|
+
run = lambda do
|
31
|
+
puts '', ' Running DataMapper.%s!' % t, ''
|
32
|
+
DataMapper.send(t + '!')
|
33
|
+
end
|
34
|
+
desc 'Run DataMapper.%s! %s' % [t, alert]
|
35
|
+
task t do
|
36
|
+
if alert
|
37
|
+
puts '', alert
|
38
|
+
puts ' ALL tables will be destroyed and recreated from ground up.'
|
39
|
+
puts ' Any data will be lost, so please consider to take some backups before continuing.', ''
|
40
|
+
puts ' Type Y and press enter to continue'
|
41
|
+
puts ' Press enter to cancel'
|
42
|
+
answer = STDIN.gets.strip
|
43
|
+
(puts 'exiting...'; exit(0)) unless answer == 'Y'
|
44
|
+
end
|
45
|
+
run.call
|
46
|
+
end
|
47
|
+
task(t + ':y'){ run.call }
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
|
2
|
+
Dir[Cfg.specs_path('**/*_spec.rb')].each {|f| require f}
|
3
|
+
|
4
|
+
specular_session = Specular.new do
|
5
|
+
boot do
|
6
|
+
include Sonar
|
7
|
+
end
|
8
|
+
before do
|
9
|
+
app App
|
10
|
+
map App.base_url
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
specular_tasks = []
|
15
|
+
App.mounted_controllers.reject do |c|
|
16
|
+
next if c.name == 'RearHomeController' || c.name =~ /::RearController\Z/
|
17
|
+
task_name = 'test:%s' % c.name
|
18
|
+
desc 'Run tests for "%s" controller' % c
|
19
|
+
task task_name do
|
20
|
+
puts specular_session.run /\A#{c}\W/
|
21
|
+
specular_session.exit_code == 0 || fail
|
22
|
+
end
|
23
|
+
c.public_actions.each do |a|
|
24
|
+
task 'test:%s#%s' % [c.name, a] do
|
25
|
+
puts specular_session.run /\A#{c}##{a}\Z/
|
26
|
+
specular_session.exit_code == 0 || fail
|
27
|
+
end
|
28
|
+
end
|
29
|
+
specular_tasks << task_name
|
30
|
+
end
|
31
|
+
|
32
|
+
task 'test:controllers' => specular_tasks
|
33
|
+
|
34
|
+
desc 'Run all tests'
|
35
|
+
task :test do
|
36
|
+
specular_session.run
|
37
|
+
puts specular_session.failures if specular_session.failed?
|
38
|
+
puts specular_session.summary
|
39
|
+
specular_session.exit_code == 0 || fail
|
40
|
+
end
|
41
|
+
task default: :test
|