shaf 0.4.1 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- checksums.yaml.gz.sig +0 -0
- data/lib/shaf/command/base.rb +51 -0
- data/lib/shaf/command/templates/Gemfile.erb +0 -1
- data/lib/shaf/command.rb +1 -47
- data/lib/shaf/extensions/resource_uris.rb +113 -151
- data/lib/shaf/generator/base.rb +61 -0
- data/lib/shaf/generator/migration/base.rb +133 -0
- data/lib/shaf/generator/migration/create_table.rb +4 -4
- data/lib/shaf/generator/migration.rb +1 -111
- data/lib/shaf/generator/serializer.rb +3 -51
- data/lib/shaf/generator/templates/api/controller.rb.erb +2 -0
- data/lib/shaf/generator/templates/api/policy.rb.erb +3 -2
- data/lib/shaf/generator/templates/api/serializer.rb.erb +2 -6
- data/lib/shaf/generator/templates/spec/integration_spec.rb.erb +30 -30
- data/lib/shaf/generator.rb +1 -57
- data/lib/shaf/helpers/cache_control.rb +21 -0
- data/lib/shaf/helpers.rb +4 -2
- data/lib/shaf/rake/db.rb +1 -1
- data/lib/shaf/upgrade/manifest.rb +29 -8
- data/lib/shaf/upgrade/package.rb +51 -27
- data/lib/shaf/version.rb +1 -1
- data/templates/api/controllers/docs_controller.rb +2 -0
- data/templates/api/controllers/root_controller.rb +2 -3
- data/templates/api/policies/base_policy.rb +3 -0
- data/templates/api/serializers/base_serializer.rb +4 -0
- data/templates/api/serializers/error_serializer.rb +3 -2
- data/templates/api/serializers/form_serializer.rb +2 -2
- data/templates/api/serializers/root_serializer.rb +3 -3
- data/templates/config/initializers/db_migrations.rb +24 -11
- data/templates/config/initializers/hal_presenter.rb +0 -5
- data/templates/config/settings.yml +8 -3
- data/upgrades/0.5.0.tar.gz +0 -0
- data.tar.gz.sig +0 -0
- metadata +69 -6
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 7ef999e9f5ceac2a21b21e33ae805a6f576bc1363c6cbd4622cbbbdf715e8252
|
4
|
+
data.tar.gz: '0030305862359ea34e55516b2d49abcd78c20eb1393ae9d99eabcb86d1b30de4'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3ac44a36ff0d58fce2e7fb58d9f85187dc75e1077cc801f5fcfd637738be364619c3ccbb3d5ea28b0cd46319f258d36a6047f4c22f53564487a4af8652569ffd
|
7
|
+
data.tar.gz: d4bddad299a6236d0930d861a2fdc054531f5892c29cc9c2f5c918066d6943c0aa888b9e2fa346a7733784f2311da5845c475632c4641949711a8f0784546d15
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'shaf/utils'
|
3
|
+
|
4
|
+
module Shaf
|
5
|
+
module Command
|
6
|
+
class ArgumentError < CommandError; end
|
7
|
+
|
8
|
+
class Base
|
9
|
+
include Utils
|
10
|
+
|
11
|
+
attr_reader :args, :options
|
12
|
+
|
13
|
+
class << self
|
14
|
+
def inherited(child)
|
15
|
+
Factory.register(child)
|
16
|
+
end
|
17
|
+
|
18
|
+
def identifier(*ids)
|
19
|
+
@identifiers = ids.flatten
|
20
|
+
end
|
21
|
+
|
22
|
+
def usage(str = nil, &block)
|
23
|
+
@usage = str || block
|
24
|
+
end
|
25
|
+
|
26
|
+
def exit_with_error(msg, status)
|
27
|
+
STDERR.puts msg
|
28
|
+
exit status
|
29
|
+
end
|
30
|
+
|
31
|
+
def options(option_parser, options); end
|
32
|
+
end
|
33
|
+
|
34
|
+
def initialize(*args)
|
35
|
+
@args = args.dup
|
36
|
+
@options = {}
|
37
|
+
parse_options!
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def parse_options!
|
43
|
+
parser = OptionParser.new
|
44
|
+
self.class.options(parser, @options)
|
45
|
+
parser.parse!(args)
|
46
|
+
rescue OptionParser::InvalidOption => e
|
47
|
+
raise ArgumentError, e.message
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/lib/shaf/command.rb
CHANGED
@@ -1,62 +1,16 @@
|
|
1
|
-
require 'optparse'
|
2
|
-
require 'shaf/utils'
|
3
1
|
require 'shaf/registrable_factory'
|
4
2
|
|
5
3
|
module Shaf
|
6
4
|
module Command
|
7
|
-
|
8
5
|
class CommandError < StandardError; end
|
9
|
-
class ArgumentError < CommandError; end
|
10
6
|
|
11
7
|
class Factory
|
12
8
|
extend RegistrableFactory
|
13
9
|
end
|
14
|
-
|
15
|
-
class Base
|
16
|
-
include Utils
|
17
|
-
|
18
|
-
attr_reader :args, :options
|
19
|
-
|
20
|
-
class << self
|
21
|
-
def inherited(child)
|
22
|
-
Factory.register(child)
|
23
|
-
end
|
24
|
-
|
25
|
-
def identifier(*ids)
|
26
|
-
@identifiers = ids.flatten
|
27
|
-
end
|
28
|
-
|
29
|
-
def usage(str = nil, &block)
|
30
|
-
@usage = str || block
|
31
|
-
end
|
32
|
-
|
33
|
-
def exit_with_error(msg, status)
|
34
|
-
STDERR.puts msg
|
35
|
-
exit status
|
36
|
-
end
|
37
|
-
|
38
|
-
def options(option_parser, options); end
|
39
|
-
end
|
40
|
-
|
41
|
-
def initialize(*args)
|
42
|
-
@args = args.dup
|
43
|
-
@options = {}
|
44
|
-
parse_options!
|
45
|
-
end
|
46
|
-
|
47
|
-
private
|
48
|
-
|
49
|
-
def parse_options!
|
50
|
-
parser = OptionParser.new
|
51
|
-
self.class.options(parser, @options)
|
52
|
-
parser.parse!(args)
|
53
|
-
rescue OptionParser::InvalidOption => e
|
54
|
-
raise ArgumentError, e.message
|
55
|
-
end
|
56
|
-
end
|
57
10
|
end
|
58
11
|
end
|
59
12
|
|
13
|
+
require 'shaf/command/base'
|
60
14
|
require 'shaf/command/new'
|
61
15
|
require 'shaf/command/upgrade'
|
62
16
|
require 'shaf/command/server'
|
@@ -9,12 +9,7 @@ module Shaf
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def register_uri(name, uri)
|
12
|
-
|
13
|
-
raise "resource uri #{name} can't be registered. Method already exist!"
|
14
|
-
end
|
15
|
-
method_string = MethodBuilder.as_string(name, uri)
|
16
|
-
UriHelperMethods.eval_method(method_string)
|
17
|
-
UriHelperMethods.register(MethodBuilder.template_method_name(name)) { uri.dup.freeze }
|
12
|
+
MethodBuilder.new(name, uri).call
|
18
13
|
|
19
14
|
include UriHelper unless self < UriHelper
|
20
15
|
end
|
@@ -39,6 +34,13 @@ module Shaf
|
|
39
34
|
def self.included(mod)
|
40
35
|
mod.extend self
|
41
36
|
end
|
37
|
+
|
38
|
+
def self.base_uri
|
39
|
+
protocol = Settings.protocol || 'http'
|
40
|
+
host = Settings.host || 'localhost'
|
41
|
+
port = Settings.port ? ":#{Settings.port}" : ""
|
42
|
+
"#{protocol}://#{host}#{port}"
|
43
|
+
end
|
42
44
|
end
|
43
45
|
|
44
46
|
# This class register uri helper methods like:
|
@@ -55,52 +57,10 @@ module Shaf
|
|
55
57
|
#
|
56
58
|
class CreateUriMethods
|
57
59
|
|
58
|
-
# Resources should never be nested more than 1 level deep.
|
59
|
-
MAX_NESTING_DEPTH = 1
|
60
|
-
|
61
|
-
class UriTemplateNestingError < StandardError
|
62
|
-
def initialize(msg = nil)
|
63
|
-
msg ||= "Uri templates only supports a nesting depth of #{MAX_NESTING_DEPTH}"
|
64
|
-
super(msg)
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
class UriTemplateVariableError < StandardError
|
69
|
-
def initialize(msg = nil)
|
70
|
-
msg ||= "Mismatch between uri templates and resources"
|
71
|
-
super(msg)
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
class << self
|
76
|
-
def resource_helper_uri(template_uri, *resources, query)
|
77
|
-
uri = replace_templates(template_uri, *resources)
|
78
|
-
query_string = MethodBuilder.query_string(query)
|
79
|
-
"#{uri}#{query_string}".freeze
|
80
|
-
end
|
81
|
-
|
82
|
-
def replace_templates(template_uri, *resources)
|
83
|
-
symbols = MethodBuilder.extract_symbols(template_uri)
|
84
|
-
resources.compact!
|
85
|
-
raise UriTemplateVariableError if symbols.size != resources.size
|
86
|
-
|
87
|
-
MethodBuilder.transform_symbols(template_uri) do |segment|
|
88
|
-
resrc = resources.shift
|
89
|
-
sym = symbols.shift
|
90
|
-
resrc.respond_to?(sym) ? resrc.public_send(sym) : resrc
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
60
|
def initialize(name, base: nil, plural_name: nil)
|
96
61
|
@name = name.to_s
|
97
62
|
@base = base&.sub(%r(/\Z), '') || ''
|
98
63
|
@plural_name = plural_name&.to_s || Utils::pluralize(name.to_s)
|
99
|
-
|
100
|
-
if nesting_depth > MAX_NESTING_DEPTH
|
101
|
-
raise UriTemplateNestingError,
|
102
|
-
"Too deep nesting level (max #{MAX_NESTING_DEPTH}): #{@base}"
|
103
|
-
end
|
104
64
|
end
|
105
65
|
|
106
66
|
def call
|
@@ -120,14 +80,12 @@ module Shaf
|
|
120
80
|
|
121
81
|
def register_resources_uri
|
122
82
|
template_uri = "#{base}/#{plural_name}".freeze
|
123
|
-
|
124
|
-
register(template_uri, helper_name)
|
83
|
+
register(plural_name, template_uri)
|
125
84
|
end
|
126
85
|
|
127
86
|
def register_resource_uri
|
128
87
|
template_uri = "#{base}/#{plural_name}/:id".freeze
|
129
|
-
|
130
|
-
register(template_uri, helper_name)
|
88
|
+
register(name, template_uri)
|
131
89
|
end
|
132
90
|
|
133
91
|
# If a resource has the same singular and plural names, then this method
|
@@ -136,137 +94,141 @@ module Shaf
|
|
136
94
|
def register_resource_uri_by_arg
|
137
95
|
resource_template_uri = "#{base}/#{plural_name}/:id"
|
138
96
|
collection_template_uri = "#{base}/#{plural_name}"
|
139
|
-
helper_name = "#{plural_name}_uri"
|
140
97
|
|
141
|
-
|
142
|
-
UriHelperMethods.register(helper_name, &block)
|
143
|
-
UriHelperMethods.register("#{helper_name}_template") do |collection = false|
|
144
|
-
(collection ? collection_template_uri : resource_template_uri).freeze
|
145
|
-
end
|
98
|
+
MethodBuilder.new(name, resource_template_uri, alt_uri: collection_template_uri).call
|
146
99
|
end
|
147
100
|
|
148
101
|
def register_new_resource_uri
|
149
102
|
template_uri = "#{base}/#{plural_name}/form".freeze
|
150
|
-
|
151
|
-
register(template_uri, helper_name)
|
103
|
+
register("new_#{name}", template_uri)
|
152
104
|
end
|
153
105
|
|
154
106
|
def register_edit_resource_uri
|
155
107
|
template_uri = "#{base}/#{plural_name}/:id/edit".freeze
|
156
|
-
|
157
|
-
register(template_uri, helper_name)
|
108
|
+
register("edit_#{name}", template_uri)
|
158
109
|
end
|
159
110
|
|
160
|
-
def register(
|
161
|
-
|
162
|
-
UriHelperMethods.register(helper_name, &block)
|
163
|
-
UriHelperMethods.register("#{helper_name}_template") { template_uri }
|
111
|
+
def register(name, template_uri)
|
112
|
+
MethodBuilder.new(name, template_uri).call
|
164
113
|
end
|
114
|
+
end
|
165
115
|
|
166
|
-
|
167
|
-
|
116
|
+
class MethodBuilder
|
117
|
+
def self.query_string(query)
|
118
|
+
return "" unless query.any?
|
119
|
+
"?#{query.map { |key,value| "#{key}=#{value}" }.join("&")}"
|
120
|
+
end
|
168
121
|
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
end
|
174
|
-
when 1
|
175
|
-
->(resrc, **query) do
|
176
|
-
CreateUriMethods.resource_helper_uri(template_uri, resrc, query)
|
177
|
-
end
|
178
|
-
when 2
|
179
|
-
->(parent_resrc, resrc, **query) do
|
180
|
-
CreateUriMethods.resource_helper_uri(template_uri, parent_resrc, resrc, query)
|
181
|
-
end
|
182
|
-
else
|
183
|
-
raise UriTemplateNestingError,
|
184
|
-
"Too deep nesting level (max #{MAX_NESTING_DEPTH}): #{template_uri}"
|
185
|
-
end
|
122
|
+
def initialize(name, uri, alt_uri: nil)
|
123
|
+
@name = name
|
124
|
+
@uri = uri
|
125
|
+
@alt_uri = alt_uri
|
186
126
|
end
|
187
127
|
|
188
|
-
def
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
end
|
197
|
-
CreateUriMethods.resource_helper_uri(*args)
|
198
|
-
}
|
199
|
-
when 1
|
200
|
-
->(parent_resrc, resrc = nil, **query) {
|
201
|
-
args = if resrc.nil?
|
202
|
-
[collection_template_uri, parent_resrc, query]
|
203
|
-
else
|
204
|
-
[resource_template_uri, parent_resrc, resrc, query]
|
205
|
-
end
|
206
|
-
CreateUriMethods.resource_helper_uri(*args)
|
207
|
-
}
|
128
|
+
def call
|
129
|
+
if UriHelper.respond_to? method_name
|
130
|
+
raise "resource uri #{@name} can't be registered. " \
|
131
|
+
"Method :#{method_name} already exist!"
|
132
|
+
end
|
133
|
+
|
134
|
+
if @alt_uri.nil?
|
135
|
+
build_methods
|
208
136
|
else
|
209
|
-
|
210
|
-
"Too deep nesting level (max #{MAX_NESTING_DEPTH}): #{template_uri}"
|
137
|
+
build_methods_with_optional_arg
|
211
138
|
end
|
212
139
|
end
|
213
140
|
|
214
|
-
|
215
|
-
|
141
|
+
private
|
142
|
+
|
143
|
+
def build_methods
|
144
|
+
UriHelperMethods.eval_method method_string
|
145
|
+
UriHelperMethods.register(template_method_name, &template_proc)
|
216
146
|
end
|
217
|
-
end
|
218
147
|
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
end
|
148
|
+
def build_methods_with_optional_arg
|
149
|
+
UriHelperMethods.eval_method method_with_optional_arg_string
|
150
|
+
UriHelperMethods.register(template_method_name, &template_proc)
|
151
|
+
end
|
224
152
|
|
225
|
-
|
226
|
-
|
227
|
-
|
153
|
+
def method_name
|
154
|
+
"#{@name}_uri".freeze
|
155
|
+
end
|
228
156
|
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
s << (args.empty? ? "**query)" : "#{args.join(', ')}, **query)")
|
233
|
-
end
|
157
|
+
def template_method_name
|
158
|
+
"#{method_name}_template".freeze
|
159
|
+
end
|
234
160
|
|
235
|
-
|
236
|
-
|
237
|
-
"?#{query.map { |key,value| "#{key}=#{value}" }.join("&")}"
|
238
|
-
end
|
161
|
+
def signature(optional_args: 0)
|
162
|
+
s = "#{method_name}("
|
239
163
|
|
240
|
-
|
241
|
-
|
242
|
-
<<~Ruby
|
243
|
-
def #{signature}
|
244
|
-
query_string = MethodBuilder.query_string(query)
|
245
|
-
\"#{interpolated_uri_string(uri)}\#{query_string}\".freeze
|
246
|
-
end
|
247
|
-
Ruby
|
248
|
-
end
|
164
|
+
symbols = extract_symbols.size.times.map { |i| "arg#{i}" }
|
165
|
+
sym_count = symbols.size
|
249
166
|
|
250
|
-
|
251
|
-
|
167
|
+
args = []
|
168
|
+
symbols.each_with_index do |arg, i|
|
169
|
+
if i < sym_count - optional_args
|
170
|
+
args << "arg#{i}"
|
171
|
+
else
|
172
|
+
args << "arg#{i} = nil"
|
173
|
+
end
|
252
174
|
end
|
175
|
+
s << (args.empty? ? "**query)" : "#{args.join(', ')}, **query)")
|
176
|
+
end
|
253
177
|
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
178
|
+
def method_string
|
179
|
+
base_uri = UriHelper.base_uri
|
180
|
+
<<~Ruby
|
181
|
+
def #{signature}
|
182
|
+
query_str = Shaf::MethodBuilder.query_string(query)
|
183
|
+
\"#{base_uri}#{interpolated_uri_string(@uri)}\#{query_str}\".freeze
|
184
|
+
end
|
185
|
+
Ruby
|
186
|
+
end
|
187
|
+
|
188
|
+
def method_with_optional_arg_string
|
189
|
+
base_uri = UriHelper.base_uri
|
190
|
+
arg_no = extract_symbols.size - 1
|
191
|
+
<<~Ruby
|
192
|
+
def #{signature(optional_args: 1)}
|
193
|
+
query_str = Shaf::MethodBuilder.query_string(query)
|
194
|
+
if arg#{arg_no}.nil?
|
195
|
+
\"#{base_uri}#{interpolated_uri_string(@alt_uri)}\#{query_str}\".freeze
|
196
|
+
else
|
197
|
+
\"#{base_uri}#{interpolated_uri_string(@uri)}\#{query_str}\".freeze
|
198
|
+
end
|
199
|
+
end
|
200
|
+
Ruby
|
201
|
+
end
|
202
|
+
|
203
|
+
def extract_symbols(uri = @uri)
|
204
|
+
uri.split('/').grep(/:.*/).map { |t| t[1..-1].to_sym }
|
205
|
+
end
|
260
206
|
|
261
|
-
|
207
|
+
def transform_symbols(uri)
|
208
|
+
i = -1
|
209
|
+
uri.split('/').map do |segment|
|
210
|
+
next segment unless segment.start_with? ':'
|
211
|
+
i += 1
|
212
|
+
yield segment, i
|
213
|
+
end.join('/')
|
214
|
+
end
|
262
215
|
|
263
|
-
|
264
|
-
|
216
|
+
def interpolated_uri_string(uri)
|
217
|
+
return uri if uri == '/'
|
265
218
|
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
219
|
+
transform_symbols(uri) do |segment, i|
|
220
|
+
sym = segment[1..-1]
|
221
|
+
"\#{arg#{i}.respond_to?(#{segment}) ? arg#{i}.#{sym} : arg#{i}}"
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
def template_proc
|
226
|
+
uri, alt_uri = @uri, @alt_uri
|
227
|
+
|
228
|
+
if alt_uri.nil?
|
229
|
+
-> { uri.freeze }
|
230
|
+
else
|
231
|
+
->(collection = false) { collection ? alt_uri : uri }
|
270
232
|
end
|
271
233
|
end
|
272
234
|
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'erb'
|
3
|
+
require 'ostruct'
|
4
|
+
|
5
|
+
module Shaf
|
6
|
+
module Generator
|
7
|
+
class Base
|
8
|
+
attr_reader :args
|
9
|
+
|
10
|
+
class << self
|
11
|
+
def inherited(child)
|
12
|
+
Factory.register(child)
|
13
|
+
end
|
14
|
+
|
15
|
+
def identifier(*ids)
|
16
|
+
@identifiers = ids.flatten
|
17
|
+
end
|
18
|
+
|
19
|
+
def usage(str = nil, &block)
|
20
|
+
@usage = str || block
|
21
|
+
end
|
22
|
+
|
23
|
+
def options(option_parser, options); end
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize(*args)
|
27
|
+
@args = args.dup
|
28
|
+
end
|
29
|
+
|
30
|
+
def call(options = {}); end
|
31
|
+
|
32
|
+
def template_dir
|
33
|
+
File.expand_path('../templates', __FILE__)
|
34
|
+
end
|
35
|
+
|
36
|
+
def read_template(file, directory = nil)
|
37
|
+
directory ||= template_dir
|
38
|
+
filename = File.join(directory, file)
|
39
|
+
filename << ".erb" unless filename.end_with?(".erb")
|
40
|
+
File.read(filename)
|
41
|
+
end
|
42
|
+
|
43
|
+
def render(template, locals = {})
|
44
|
+
str = read_template(template)
|
45
|
+
locals[:changes] ||= []
|
46
|
+
b = OpenStruct.new(locals).instance_eval { binding }
|
47
|
+
ERB.new(str, 0, '%-<>').result(b)
|
48
|
+
rescue SystemCallError => e
|
49
|
+
puts "Failed to render template #{template}: #{e.message}"
|
50
|
+
raise
|
51
|
+
end
|
52
|
+
|
53
|
+
def write_output(file, content)
|
54
|
+
dir = File.dirname(file)
|
55
|
+
FileUtils.mkdir_p(dir) unless Dir.exist?(dir)
|
56
|
+
File.write(file, content)
|
57
|
+
puts "Added: #{file}"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
3
|
+
module Shaf
|
4
|
+
module Generator
|
5
|
+
module Migration
|
6
|
+
class Base
|
7
|
+
DB_COL_FORMAT_STRINGS = {
|
8
|
+
integer: ['Integer :%s', 'add_column :%s, Integer'],
|
9
|
+
varchar: ['String %s', 'add_column :%s, String'],
|
10
|
+
string: ['String :%s', 'add_column :%s, String'],
|
11
|
+
text: ['String :%s, text: true', 'add_column :%s, String, text: true'],
|
12
|
+
blob: ['File :%s', 'add_column :%s, File'],
|
13
|
+
bigint: ['Bignum :%s', 'add_column :%s, Bignum'],
|
14
|
+
double: ['Float :%s', 'add_column :%s, Float'],
|
15
|
+
numeric: ['BigDecimal :%s', 'add_column :%s, BigDecimal'],
|
16
|
+
date: ['Date :%s', 'add_column :%s, Date'],
|
17
|
+
timestamp: ['DateTime :%s', 'add_column :%s, DateTime'],
|
18
|
+
time: ['Time :%s', 'add_column :%s, Time'],
|
19
|
+
bool: ['TrueClass :%s', 'add_column :%s, TrueClass'],
|
20
|
+
boolean: ['TrueClass :%s', 'add_column :%s, TrueClass'],
|
21
|
+
index: ['index :%s, unique: true', 'add_index :%s'],
|
22
|
+
}
|
23
|
+
|
24
|
+
COMPLEX_DB_TYPES = [
|
25
|
+
{
|
26
|
+
pattern: /\Aforeign_key\((\w+)\)/,
|
27
|
+
strings: ['foreign_key :%s, :\1', 'add_foreign_key :%s, :\1'],
|
28
|
+
validator: ->(type, match) {
|
29
|
+
break if ::DB.table_exists?(match[1])
|
30
|
+
["Foreign key table '#{match[1]}' does not exist!"]
|
31
|
+
}
|
32
|
+
}
|
33
|
+
]
|
34
|
+
|
35
|
+
attr_reader :args
|
36
|
+
|
37
|
+
class << self
|
38
|
+
def inherited(child)
|
39
|
+
Factory.register(child)
|
40
|
+
end
|
41
|
+
|
42
|
+
def identifier(*ids)
|
43
|
+
@identifiers = ids.flatten.map(&:to_s)
|
44
|
+
end
|
45
|
+
|
46
|
+
def usage(str = nil, &block)
|
47
|
+
@usage = str || block
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def initialize(*args)
|
52
|
+
@args = args.dup
|
53
|
+
end
|
54
|
+
|
55
|
+
def call
|
56
|
+
validate_args
|
57
|
+
name = compile_migration_name
|
58
|
+
compile_changes
|
59
|
+
[target(name), render]
|
60
|
+
rescue StandardError => e
|
61
|
+
raise Command::ArgumentError, e.message
|
62
|
+
end
|
63
|
+
|
64
|
+
def add_change(change)
|
65
|
+
@changes ||= []
|
66
|
+
@changes << change if change
|
67
|
+
end
|
68
|
+
|
69
|
+
def column_def(str, create: true)
|
70
|
+
name, type = str.split(':')
|
71
|
+
format_string = db_format_string(type, create ? 0 : 1)
|
72
|
+
format format_string, name.downcase
|
73
|
+
end
|
74
|
+
|
75
|
+
def target(name)
|
76
|
+
raise "Migration filename is nil" unless name
|
77
|
+
"db/migrations/#{timestamp}_#{name}.rb"
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def timestamp
|
83
|
+
DateTime.now.strftime("%Y%m%d%H%M%S")
|
84
|
+
end
|
85
|
+
|
86
|
+
def add_timestamp_columns?
|
87
|
+
if File.exist? 'config/initializers/sequel.rb'
|
88
|
+
require 'config/initializers/sequel'
|
89
|
+
Sequel::Model.plugins.include? Sequel::Plugins::Timestamps
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def db_format_string(type, range = 0..1)
|
94
|
+
type ||= :string
|
95
|
+
result = DB_COL_FORMAT_STRINGS[type.to_sym]
|
96
|
+
result ||= regexp_db_format_string(type)
|
97
|
+
raise "Column type '#{type}' not supported" unless result
|
98
|
+
result[range]
|
99
|
+
end
|
100
|
+
|
101
|
+
def regexp_db_format_string(type)
|
102
|
+
COMPLEX_DB_TYPES.each do |complex_type|
|
103
|
+
match = complex_type[:pattern].match(type) or next
|
104
|
+
validator = complex_type[:validator]
|
105
|
+
errors = validator&.call(type, match)
|
106
|
+
if errors.nil? || errors.empty?
|
107
|
+
return complex_type[:strings].map { |s| replace_backreferences(match, s) }
|
108
|
+
end
|
109
|
+
raise "Failed to process '#{type}': #{errors.join(', ')}"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def replace_backreferences(match, str)
|
114
|
+
groups = match.size
|
115
|
+
(1...groups).inject(str) do |s, i|
|
116
|
+
s.gsub("\\#{i}", match[i])
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def render
|
121
|
+
<<~EOS
|
122
|
+
Sequel.migration do
|
123
|
+
change do
|
124
|
+
#{@changes.flatten.join("\n ")}
|
125
|
+
end
|
126
|
+
end
|
127
|
+
EOS
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
@@ -11,14 +11,14 @@ module Shaf
|
|
11
11
|
raise "Please provide a table name when generation a create table migration"
|
12
12
|
end
|
13
13
|
|
14
|
-
def compile_migration_name
|
15
|
-
"create_#{table_name}_table"
|
16
|
-
end
|
17
|
-
|
18
14
|
def table_name
|
19
15
|
args.first || ""
|
20
16
|
end
|
21
17
|
|
18
|
+
def compile_migration_name
|
19
|
+
"create_#{table_name}_table"
|
20
|
+
end
|
21
|
+
|
22
22
|
def compile_changes
|
23
23
|
add_change create_table_change
|
24
24
|
end
|