my_enginery 0.2.8
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/.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
|