shaf 0.4.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data/lib/shaf/command/base.rb +51 -0
  4. data/lib/shaf/command/templates/Gemfile.erb +0 -1
  5. data/lib/shaf/command.rb +1 -47
  6. data/lib/shaf/extensions/resource_uris.rb +113 -151
  7. data/lib/shaf/generator/base.rb +61 -0
  8. data/lib/shaf/generator/migration/base.rb +133 -0
  9. data/lib/shaf/generator/migration/create_table.rb +4 -4
  10. data/lib/shaf/generator/migration.rb +1 -111
  11. data/lib/shaf/generator/serializer.rb +3 -51
  12. data/lib/shaf/generator/templates/api/controller.rb.erb +2 -0
  13. data/lib/shaf/generator/templates/api/policy.rb.erb +3 -2
  14. data/lib/shaf/generator/templates/api/serializer.rb.erb +2 -6
  15. data/lib/shaf/generator/templates/spec/integration_spec.rb.erb +30 -30
  16. data/lib/shaf/generator.rb +1 -57
  17. data/lib/shaf/helpers/cache_control.rb +21 -0
  18. data/lib/shaf/helpers.rb +4 -2
  19. data/lib/shaf/rake/db.rb +1 -1
  20. data/lib/shaf/upgrade/manifest.rb +29 -8
  21. data/lib/shaf/upgrade/package.rb +51 -27
  22. data/lib/shaf/version.rb +1 -1
  23. data/templates/api/controllers/docs_controller.rb +2 -0
  24. data/templates/api/controllers/root_controller.rb +2 -3
  25. data/templates/api/policies/base_policy.rb +3 -0
  26. data/templates/api/serializers/base_serializer.rb +4 -0
  27. data/templates/api/serializers/error_serializer.rb +3 -2
  28. data/templates/api/serializers/form_serializer.rb +2 -2
  29. data/templates/api/serializers/root_serializer.rb +3 -3
  30. data/templates/config/initializers/db_migrations.rb +24 -11
  31. data/templates/config/initializers/hal_presenter.rb +0 -5
  32. data/templates/config/settings.yml +8 -3
  33. data/upgrades/0.5.0.tar.gz +0 -0
  34. data.tar.gz.sig +0 -0
  35. metadata +69 -6
  36. metadata.gz.sig +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 33b4f2f8ba33dcc7d3f8abe950745897fca2439e
4
- data.tar.gz: d677b6e4db2843c764027e91b00ee384a24baa60
2
+ SHA256:
3
+ metadata.gz: 7ef999e9f5ceac2a21b21e33ae805a6f576bc1363c6cbd4622cbbbdf715e8252
4
+ data.tar.gz: '0030305862359ea34e55516b2d49abcd78c20eb1393ae9d99eabcb86d1b30de4'
5
5
  SHA512:
6
- metadata.gz: 24eaf2fb8f70443206460d48853ea44d304c1616a65be60defb33147ee09e53923ee13b3563efc0ad00ce41976d7acd6048047d3b1f6d0323341fc9946900546
7
- data.tar.gz: 7714930986d85adea752deb9bd7c200a3a7f06cb10826f4e39c9b066dd96b4fd58bae7e327c8fe8bc5e276ab781a9112e8d123e2787a4676fec09a06d10954fd
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
@@ -1,5 +1,4 @@
1
1
  # frozen_string_literal: true
2
- # A sample Gemfile
3
2
  source "https://rubygems.org"
4
3
  ruby '<%= RUBY_VERSION %>'
5
4
 
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
- if UriHelper.respond_to? MethodBuilder.method_name(name)
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
- helper_name = "#{plural_name}_uri".freeze
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
- helper_name = "#{name}_uri".freeze
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
- block = resource_or_collection_method_proc(resource_template_uri, collection_template_uri)
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
- helper_name = "new_#{name}_uri".freeze
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
- helper_name = "edit_#{name}_uri".freeze
157
- register(template_uri, helper_name)
108
+ register("edit_#{name}", template_uri)
158
109
  end
159
110
 
160
- def register(template_uri, helper_name)
161
- block = resource_helper_method_proc(template_uri)
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
- def resource_helper_method_proc(template_uri)
167
- arg_count = MethodBuilder.extract_symbols(template_uri).size
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
- case arg_count
170
- when 0
171
- ->(**query) do
172
- CreateUriMethods.resource_helper_uri(template_uri, query)
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 resource_or_collection_method_proc(resource_template_uri, collection_template_uri)
189
- case nesting_depth
190
- when 0
191
- ->(resrc = nil, **query) {
192
- args = if resrc.nil?
193
- [collection_template_uri, query]
194
- else
195
- [resource_template_uri, resrc, query]
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
- raise UriTemplateNestingError,
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
- def nesting_depth
215
- MethodBuilder.extract_symbols(base).size
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
- module MethodBuilder
220
- class << self
221
- def method_name(name)
222
- "#{name}_uri"
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
- def template_method_name(name)
226
- "#{method_name(name)}_template"
227
- end
153
+ def method_name
154
+ "#{@name}_uri".freeze
155
+ end
228
156
 
229
- def signature(name, uri)
230
- args = extract_symbols(uri)
231
- s = "#{method_name(name)}("
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
- def query_string(query)
236
- return "" unless query.any?
237
- "?#{query.map { |key,value| "#{key}=#{value}" }.join("&")}"
238
- end
161
+ def signature(optional_args: 0)
162
+ s = "#{method_name}("
239
163
 
240
- def as_string(name, uri)
241
- signature = signature(name, uri)
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
- def extract_symbols(uri)
251
- uri.split('/').grep(/:.*/).map { |t| t[1..-1] }.map(&:to_sym)
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
- def transform_symbols(uri)
255
- uri.split('/').map do |segment|
256
- next segment unless segment.start_with? ':'
257
- yield segment
258
- end.join('/')
259
- end
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
- private
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
- def interpolated_uri_string(uri)
264
- return uri if uri == '/'
216
+ def interpolated_uri_string(uri)
217
+ return uri if uri == '/'
265
218
 
266
- transform_symbols(uri) do |segment|
267
- str = segment[1..-1]
268
- "\#{#{str}.respond_to?(#{segment}) ? #{str}.#{str} : #{str}}"
269
- end
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