shaf 0.3.1 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/bin/shaf +3 -0
- data/lib/shaf.rb +1 -0
- data/lib/shaf/api_doc/document.rb +0 -1
- data/lib/shaf/app.rb +1 -1
- data/lib/shaf/command.rb +19 -2
- data/lib/shaf/command/generate.rb +11 -1
- data/lib/shaf/command/new.rb +1 -6
- data/lib/shaf/command/server.rb +7 -0
- data/lib/shaf/command/upgrade.rb +38 -0
- data/lib/shaf/extensions/authorize.rb +8 -6
- data/lib/shaf/extensions/resource_uris.rb +129 -71
- data/lib/shaf/generator.rb +4 -0
- data/lib/shaf/generator/controller.rb +2 -2
- data/lib/shaf/generator/migration.rb +33 -14
- data/lib/shaf/generator/migration/add_column.rb +1 -3
- data/lib/shaf/generator/migration/add_index.rb +42 -0
- data/lib/shaf/generator/model.rb +4 -4
- data/lib/shaf/generator/policy.rb +1 -1
- data/lib/shaf/generator/scaffold.rb +3 -3
- data/lib/shaf/generator/serializer.rb +5 -5
- data/lib/shaf/rake.rb +5 -0
- data/lib/shaf/rake/db.rb +79 -0
- data/lib/shaf/rake/test.rb +32 -0
- data/lib/shaf/registrable_factory.rb +8 -3
- data/lib/shaf/settings.rb +20 -4
- data/lib/shaf/tasks.rb +6 -3
- data/lib/shaf/{api_doc/task.rb → tasks/api_doc_task.rb} +6 -5
- data/lib/shaf/tasks/db_task.rb +42 -0
- data/lib/shaf/tasks/test_task.rb +16 -0
- data/lib/shaf/upgrade.rb +3 -0
- data/lib/shaf/upgrade/manifest.rb +31 -0
- data/lib/shaf/upgrade/package.rb +158 -0
- data/lib/shaf/upgrade/version.rb +52 -0
- data/lib/shaf/utils.rb +30 -0
- data/lib/shaf/version.rb +1 -1
- data/templates/Rakefile +2 -3
- data/templates/config/database.rb +3 -3
- metadata +33 -202
- metadata.gz.sig +0 -0
- data/lib/shaf/api_doc.rb +0 -3
- data/lib/shaf/tasks/db.rb +0 -81
- data/lib/shaf/tasks/test.rb +0 -48
data/lib/shaf/generator.rb
CHANGED
@@ -24,12 +24,16 @@ module Shaf
|
|
24
24
|
def usage(str = nil, &block)
|
25
25
|
@usage = str || block
|
26
26
|
end
|
27
|
+
|
28
|
+
def options(option_parser, options); end
|
27
29
|
end
|
28
30
|
|
29
31
|
def initialize(*args)
|
30
32
|
@args = args.dup
|
31
33
|
end
|
32
34
|
|
35
|
+
def call(options = {}); end
|
36
|
+
|
33
37
|
def template_dir
|
34
38
|
File.expand_path('../generator/templates', __FILE__)
|
35
39
|
end
|
@@ -5,9 +5,9 @@ module Shaf
|
|
5
5
|
identifier :controller
|
6
6
|
usage 'generate controller RESOURCE_NAME [attribute:type] [..]'
|
7
7
|
|
8
|
-
def call
|
8
|
+
def call(options = {})
|
9
9
|
create_controller
|
10
|
-
create_integration_spec
|
10
|
+
create_integration_spec if options[:specs]
|
11
11
|
add_link_to_root
|
12
12
|
end
|
13
13
|
|
@@ -23,19 +23,24 @@ module Shaf
|
|
23
23
|
|
24
24
|
class Base
|
25
25
|
DB_COL_TYPES = {
|
26
|
-
integer: ['Integer :%s',
|
27
|
-
varchar: ['String %s',
|
28
|
-
string: ['String :%s',
|
29
|
-
text: ['String :%s, text: true',
|
30
|
-
blob: ['File :%s',
|
31
|
-
bigint: ['Bignum :%s',
|
32
|
-
double: ['Float :%s',
|
33
|
-
numeric: ['BigDecimal :%s',
|
34
|
-
date: ['Date :%s',
|
35
|
-
timestamp: ['DateTime :%s',
|
36
|
-
time: ['Time :%s',
|
37
|
-
bool: ['TrueClass :%s',
|
38
|
-
boolean: ['TrueClass :%s',
|
26
|
+
integer: ['Integer :%s', 'add_column :%s, Integer'],
|
27
|
+
varchar: ['String %s', 'add_column :%s, String'],
|
28
|
+
string: ['String :%s', 'add_column :%s, String'],
|
29
|
+
text: ['String :%s, text: true', 'add_column :%s, String, text: true'],
|
30
|
+
blob: ['File :%s', 'add_column :%s, File'],
|
31
|
+
bigint: ['Bignum :%s', 'add_column :%s, Bignum'],
|
32
|
+
double: ['Float :%s', 'add_column :%s, Float'],
|
33
|
+
numeric: ['BigDecimal :%s', 'add_column :%s, BigDecimal'],
|
34
|
+
date: ['Date :%s', 'add_column :%s, Date'],
|
35
|
+
timestamp: ['DateTime :%s', 'add_column :%s, DateTime'],
|
36
|
+
time: ['Time :%s', 'add_column :%s, Time'],
|
37
|
+
bool: ['TrueClass :%s', 'add_column :%s, TrueClass'],
|
38
|
+
boolean: ['TrueClass :%s', 'add_column :%s, TrueClass'],
|
39
|
+
index: ['index :%s, unique: true', 'add_index :%s'],
|
40
|
+
}
|
41
|
+
|
42
|
+
REGEXP_DB_TYPES = {
|
43
|
+
/\Aforeign_key\((\w+)\)/ => ['foreign_key :%s, :\1', 'add_foreign_key :%s, :\1'],
|
39
44
|
}
|
40
45
|
|
41
46
|
attr_reader :args
|
@@ -74,7 +79,13 @@ module Shaf
|
|
74
79
|
|
75
80
|
def db_type(type)
|
76
81
|
type ||= :string
|
77
|
-
DB_COL_TYPES[type.to_sym]
|
82
|
+
result = DB_COL_TYPES[type.to_sym]
|
83
|
+
result ||= REGEXP_DB_TYPES.each do |pattern, strings|
|
84
|
+
m = pattern.match(type) or next
|
85
|
+
break strings.map { |a| replace_backreferences(m, a) }
|
86
|
+
end
|
87
|
+
raise "Column type '#{type}' not supported" unless result
|
88
|
+
result
|
78
89
|
end
|
79
90
|
|
80
91
|
def column_def(str, create: true)
|
@@ -93,6 +104,13 @@ module Shaf
|
|
93
104
|
DateTime.now.strftime("%Y%m%d%H%M%S")
|
94
105
|
end
|
95
106
|
|
107
|
+
def replace_backreferences(match, str)
|
108
|
+
groups = match.size
|
109
|
+
(1...groups).inject(str) do |s, i|
|
110
|
+
s.gsub("\\#{i}", match[i])
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
96
114
|
def add_timestamp_columns?
|
97
115
|
if File.exist? 'config/initializers/sequel.rb'
|
98
116
|
require 'config/initializers/sequel'
|
@@ -116,6 +134,7 @@ module Shaf
|
|
116
134
|
end
|
117
135
|
|
118
136
|
require 'shaf/generator/migration/add_column'
|
137
|
+
require 'shaf/generator/migration/add_index'
|
119
138
|
require 'shaf/generator/migration/create_table'
|
120
139
|
require 'shaf/generator/migration/drop_column'
|
121
140
|
require 'shaf/generator/migration/empty'
|
@@ -34,9 +34,7 @@ module Shaf
|
|
34
34
|
end
|
35
35
|
|
36
36
|
def add_columns_change
|
37
|
-
cols = columns.map
|
38
|
-
"add_column #{column_def(s, create: false)}"
|
39
|
-
end
|
37
|
+
cols = columns.map { |s| column_def(s, create: false) }
|
40
38
|
[
|
41
39
|
"alter_table(:#{table_name}) do",
|
42
40
|
*cols.map { |col| col.prepend(" ") }, # indent body with 2 spaces
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Shaf
|
2
|
+
module Generator
|
3
|
+
module Migration
|
4
|
+
class AddIndex < Base
|
5
|
+
|
6
|
+
identifier %w(add index)
|
7
|
+
usage 'generate migration add index TABLE_NAME COLUMN_NAME'
|
8
|
+
|
9
|
+
def validate_args
|
10
|
+
if (table_name || "").empty? || (column || "").empty?
|
11
|
+
raise "Please provide a table and the column to create index on"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def compile_migration_name
|
16
|
+
"add_#{column}_index_to_#{table_name}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def table_name
|
20
|
+
args.first
|
21
|
+
end
|
22
|
+
|
23
|
+
def compile_changes
|
24
|
+
add_change add_index_change
|
25
|
+
end
|
26
|
+
|
27
|
+
def column
|
28
|
+
args[1]
|
29
|
+
end
|
30
|
+
|
31
|
+
def add_index_change
|
32
|
+
col_def = column_def("#{column}:index", create: false)
|
33
|
+
[
|
34
|
+
"alter_table(:#{table_name}) do",
|
35
|
+
col_def.prepend(" "), # indent body with 2 spaces
|
36
|
+
"end\n"
|
37
|
+
]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/shaf/generator/model.rb
CHANGED
@@ -5,10 +5,10 @@ module Shaf
|
|
5
5
|
identifier :model
|
6
6
|
usage 'generate model MODEL_NAME [attribute:type] [..]'
|
7
7
|
|
8
|
-
def call
|
8
|
+
def call(options = {})
|
9
9
|
create_model
|
10
10
|
create_migration
|
11
|
-
create_serializer
|
11
|
+
create_serializer(options)
|
12
12
|
end
|
13
13
|
|
14
14
|
def model_name
|
@@ -61,10 +61,10 @@ module Shaf
|
|
61
61
|
Migration::Generator.new(*migration_args).call
|
62
62
|
end
|
63
63
|
|
64
|
-
def create_serializer
|
64
|
+
def create_serializer(options)
|
65
65
|
serializer_args = %W(serializer #{model_name})
|
66
66
|
serializer_args += args[1..-1].map { |arg| arg.split(':').first }
|
67
|
-
Generator::Factory.create(*serializer_args).call
|
67
|
+
Generator::Factory.create(*serializer_args).call(options)
|
68
68
|
end
|
69
69
|
end
|
70
70
|
end
|
@@ -5,13 +5,13 @@ module Shaf
|
|
5
5
|
identifier :scaffold
|
6
6
|
usage 'generate scaffold RESOURCE_NAME [attribute:type] [..]'
|
7
7
|
|
8
|
-
def call
|
8
|
+
def call(options = {})
|
9
9
|
if name.empty?
|
10
10
|
raise "Please provide a resource name when using the scaffold generator!"
|
11
11
|
end
|
12
12
|
|
13
|
-
Generator::Factory.create('model', *args).call
|
14
|
-
Generator::Factory.create('controller', *controller_args).call
|
13
|
+
Generator::Factory.create('model', *args).call(options)
|
14
|
+
Generator::Factory.create('controller', *controller_args).call(options)
|
15
15
|
end
|
16
16
|
|
17
17
|
def name
|
@@ -4,10 +4,10 @@ module Shaf
|
|
4
4
|
identifier :serializer
|
5
5
|
usage 'generate serializer MODEL_NAME [attribute] [..]'
|
6
6
|
|
7
|
-
def call
|
7
|
+
def call(options = {})
|
8
8
|
create_serializer
|
9
|
-
create_serializer_spec
|
10
|
-
create_policy
|
9
|
+
create_serializer_spec if options[:specs]
|
10
|
+
create_policy(options)
|
11
11
|
end
|
12
12
|
|
13
13
|
def name
|
@@ -248,9 +248,9 @@ module Shaf
|
|
248
248
|
}
|
249
249
|
end
|
250
250
|
|
251
|
-
def create_policy
|
251
|
+
def create_policy(options)
|
252
252
|
policy_args = ["policy", name, *args[1..-1]]
|
253
|
-
Generator::Factory.create(*policy_args).call
|
253
|
+
Generator::Factory.create(*policy_args).call(options)
|
254
254
|
end
|
255
255
|
end
|
256
256
|
end
|
data/lib/shaf/rake.rb
ADDED
data/lib/shaf/rake/db.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
Shaf::DbTask.new(:version, description: "Prints current schema version") do
|
2
|
+
if migrations.any?
|
3
|
+
version, filename = extract_version_and_filename(last_migration)
|
4
|
+
puts "Schema version: #{version} (#{filename})"
|
5
|
+
else
|
6
|
+
puts "No migrations found"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
Shaf::DbTask.new(:versions, description: "Prints all schema versions") do
|
11
|
+
if migrations.any?
|
12
|
+
migrations.each do |migration|
|
13
|
+
version, filename = extract_version_and_filename(migration)
|
14
|
+
next unless version && filename
|
15
|
+
puts "#{version}: #{filename}"
|
16
|
+
end
|
17
|
+
else
|
18
|
+
puts "No migrations found"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
Shaf::DbTask.new(:migrate, description: "Run migrations", args: [:version]) do |t, args|
|
23
|
+
if args[:version]
|
24
|
+
puts "Migrating to version #{args[:version]}"
|
25
|
+
Sequel::Migrator.run(DB, MIGRATIONS_DIR, target: args[:version].to_i)
|
26
|
+
else
|
27
|
+
puts "Migrating to latest"
|
28
|
+
Sequel::Migrator.run(DB, MIGRATIONS_DIR)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
Shaf::DbTask.new(
|
33
|
+
:rollback,
|
34
|
+
description: "Perform rollback to specified number of steps back or to the previous version",
|
35
|
+
args: [:target]
|
36
|
+
) do |t, args|
|
37
|
+
|
38
|
+
args.with_defaults(target: 1)
|
39
|
+
target = -(args[:target].to_i + 1)
|
40
|
+
target = 0 if -target > migrations.size
|
41
|
+
migration = migrations.dig(target, :filename)
|
42
|
+
version, _ = extract_version_and_filename(migration)
|
43
|
+
|
44
|
+
warn_if_rolling_back_more_than = 5
|
45
|
+
if target == 0 || -target > warn_if_rolling_back_more_than
|
46
|
+
puts "This would migrate the Database to version: #{version}. Continue [N/y]?"
|
47
|
+
next unless /\Ay/i =~ STDIN.gets.chomp&.downcase
|
48
|
+
end
|
49
|
+
Sequel::Migrator.run(DB, MIGRATIONS_DIR, target: version.to_i)
|
50
|
+
end
|
51
|
+
|
52
|
+
Rake::Task["db:migrate"].enhance do
|
53
|
+
Rake::Task["db:version"].invoke
|
54
|
+
end
|
55
|
+
|
56
|
+
Rake::Task["db:rollback"].enhance do
|
57
|
+
Rake::Task["db:version"].invoke
|
58
|
+
end
|
59
|
+
|
60
|
+
Shaf::DbTask.new(:reset, description: "Reset the database by deleting all rows in all columns") do
|
61
|
+
version = 0
|
62
|
+
Sequel::Migrator.run(DB, MIGRATIONS_DIR, target: version)
|
63
|
+
Sequel::Migrator.run(DB, MIGRATIONS_DIR)
|
64
|
+
end
|
65
|
+
|
66
|
+
Shaf::DbTask.new(:seed, description: "Seed the Database") do
|
67
|
+
ENV['RACK_ENV'] ||= 'development'
|
68
|
+
require 'config/bootstrap'
|
69
|
+
|
70
|
+
if File.exist? "db/seeds.rb"
|
71
|
+
require "db/seeds"
|
72
|
+
end
|
73
|
+
|
74
|
+
if Dir.exist? "db/seeds"
|
75
|
+
Dir['db/seeds/**/*.rb'].each do |file|
|
76
|
+
require file.sub(".rb", "")
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'shaf/tasks'
|
2
|
+
|
3
|
+
namespace :test do |ns|
|
4
|
+
Shaf::TestTask.new(:integration) do |t|
|
5
|
+
t.pattern = "spec/integration/**/*_spec.rb"
|
6
|
+
end
|
7
|
+
|
8
|
+
Shaf::TestTask.new(:models) do |t|
|
9
|
+
t.pattern = "spec/models/**/*_spec.rb"
|
10
|
+
end
|
11
|
+
|
12
|
+
Shaf::TestTask.new(:serializers) do |t|
|
13
|
+
t.pattern = "spec/serializers/**/*_spec.rb"
|
14
|
+
end
|
15
|
+
|
16
|
+
Shaf::TestTask.new(:lib) do |t|
|
17
|
+
t.pattern = "spec/lib/**/*_spec.rb"
|
18
|
+
end
|
19
|
+
|
20
|
+
Shaf::TestTask.new(:all) do |t|
|
21
|
+
t.pattern = [
|
22
|
+
"spec/lib/**/*_spec.rb",
|
23
|
+
"spec/models/**/*_spec.rb",
|
24
|
+
"spec/serializers/**/*_spec.rb",
|
25
|
+
"spec/integration/**/*_spec.rb"
|
26
|
+
]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
desc "Run all tests"
|
31
|
+
task test: 'test:all'
|
32
|
+
|
@@ -7,6 +7,11 @@ module Shaf
|
|
7
7
|
reg.dup
|
8
8
|
end
|
9
9
|
|
10
|
+
def each
|
11
|
+
return all.each unless block_given?
|
12
|
+
all.each { |c| yield c }
|
13
|
+
end
|
14
|
+
|
10
15
|
def size
|
11
16
|
reg.size
|
12
17
|
end
|
@@ -29,7 +34,7 @@ module Shaf
|
|
29
34
|
|
30
35
|
def usage
|
31
36
|
reg.compact.map do |entry|
|
32
|
-
usage = entry.
|
37
|
+
usage = entry.instance_variable_get(:@usage)
|
33
38
|
usage.respond_to?(:call) ? usage.call : usage
|
34
39
|
end
|
35
40
|
end
|
@@ -49,7 +54,7 @@ module Shaf
|
|
49
54
|
end
|
50
55
|
|
51
56
|
def matching_class?(strings, clazz)
|
52
|
-
identifiers = clazz.
|
57
|
+
identifiers = clazz.instance_variable_get(:@identifiers)
|
53
58
|
return false if strings.size < identifiers.size
|
54
59
|
identifiers.zip(strings).all? { |pattern, str| matching_identifier? str, pattern }
|
55
60
|
end
|
@@ -62,7 +67,7 @@ module Shaf
|
|
62
67
|
end
|
63
68
|
|
64
69
|
def identifier_count(clazz)
|
65
|
-
clazz.
|
70
|
+
clazz.instance_variable_get(:@identifiers)&.size || 0
|
66
71
|
end
|
67
72
|
|
68
73
|
def init_args(clazz, params)
|
data/lib/shaf/settings.rb
CHANGED
@@ -18,16 +18,32 @@ module Shaf
|
|
18
18
|
def method_missing(method, *args)
|
19
19
|
load unless defined? @settings
|
20
20
|
|
21
|
-
|
22
|
-
|
21
|
+
if method.to_s.end_with? "="
|
22
|
+
define_setter(method)
|
23
|
+
public_send(method, args.first)
|
24
|
+
else
|
25
|
+
define_getter(method)
|
26
|
+
public_send(method)
|
23
27
|
end
|
24
|
-
|
25
|
-
return public_send(method)
|
26
28
|
end
|
27
29
|
|
28
30
|
def respond_to_missing?(method, include_private = false)
|
29
31
|
return true
|
30
32
|
end
|
33
|
+
|
34
|
+
def define_getter(method)
|
35
|
+
define_singleton_method(method) do
|
36
|
+
@settings.dig(env.to_s, method.to_s)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def define_setter(method)
|
41
|
+
define_singleton_method(method) do |arg|
|
42
|
+
key = method[0..-2]
|
43
|
+
@settings[env.to_s] ||= {}
|
44
|
+
@settings[env.to_s][key] = arg
|
45
|
+
end
|
46
|
+
end
|
31
47
|
end
|
32
48
|
end
|
33
49
|
end
|
data/lib/shaf/tasks.rb
CHANGED