shaf 0.3.1 → 0.4.0

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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/bin/shaf +3 -0
  5. data/lib/shaf.rb +1 -0
  6. data/lib/shaf/api_doc/document.rb +0 -1
  7. data/lib/shaf/app.rb +1 -1
  8. data/lib/shaf/command.rb +19 -2
  9. data/lib/shaf/command/generate.rb +11 -1
  10. data/lib/shaf/command/new.rb +1 -6
  11. data/lib/shaf/command/server.rb +7 -0
  12. data/lib/shaf/command/upgrade.rb +38 -0
  13. data/lib/shaf/extensions/authorize.rb +8 -6
  14. data/lib/shaf/extensions/resource_uris.rb +129 -71
  15. data/lib/shaf/generator.rb +4 -0
  16. data/lib/shaf/generator/controller.rb +2 -2
  17. data/lib/shaf/generator/migration.rb +33 -14
  18. data/lib/shaf/generator/migration/add_column.rb +1 -3
  19. data/lib/shaf/generator/migration/add_index.rb +42 -0
  20. data/lib/shaf/generator/model.rb +4 -4
  21. data/lib/shaf/generator/policy.rb +1 -1
  22. data/lib/shaf/generator/scaffold.rb +3 -3
  23. data/lib/shaf/generator/serializer.rb +5 -5
  24. data/lib/shaf/rake.rb +5 -0
  25. data/lib/shaf/rake/db.rb +79 -0
  26. data/lib/shaf/rake/test.rb +32 -0
  27. data/lib/shaf/registrable_factory.rb +8 -3
  28. data/lib/shaf/settings.rb +20 -4
  29. data/lib/shaf/tasks.rb +6 -3
  30. data/lib/shaf/{api_doc/task.rb → tasks/api_doc_task.rb} +6 -5
  31. data/lib/shaf/tasks/db_task.rb +42 -0
  32. data/lib/shaf/tasks/test_task.rb +16 -0
  33. data/lib/shaf/upgrade.rb +3 -0
  34. data/lib/shaf/upgrade/manifest.rb +31 -0
  35. data/lib/shaf/upgrade/package.rb +158 -0
  36. data/lib/shaf/upgrade/version.rb +52 -0
  37. data/lib/shaf/utils.rb +30 -0
  38. data/lib/shaf/version.rb +1 -1
  39. data/templates/Rakefile +2 -3
  40. data/templates/config/database.rb +3 -3
  41. metadata +33 -202
  42. metadata.gz.sig +0 -0
  43. data/lib/shaf/api_doc.rb +0 -3
  44. data/lib/shaf/tasks/db.rb +0 -81
  45. data/lib/shaf/tasks/test.rb +0 -48
@@ -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', ':%s, Integer'],
27
- varchar: ['String %s', ':%s, String'],
28
- string: ['String :%s', ':%s, String'],
29
- text: ['String :%s, text: true', ':%s, String, text: true'],
30
- blob: ['File :%s', ':%s, File'],
31
- bigint: ['Bignum :%s', ':%s, Bignum'],
32
- double: ['Float :%s', ':%s, Float'],
33
- numeric: ['BigDecimal :%s', ':%s, BigDecimal'],
34
- date: ['Date :%s', ':%s, Date'],
35
- timestamp: ['DateTime :%s', ':%s, DateTime'],
36
- time: ['Time :%s', ':%s, Time'],
37
- bool: ['TrueClass :%s', ':%s, TrueClass'],
38
- boolean: ['TrueClass :%s', ':%s, TrueClass'],
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] or raise "Column type '#{type}' not supported"
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 do |s|
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
@@ -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
@@ -4,7 +4,7 @@ module Shaf
4
4
  identifier :policy
5
5
  usage 'generate policy MODEL_NAME [attribute] [..]'
6
6
 
7
- def call
7
+ def call(options = {})
8
8
  create_policy
9
9
  end
10
10
 
@@ -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
@@ -0,0 +1,5 @@
1
+ require 'shaf/settings'
2
+ require 'shaf/tasks'
3
+ require 'shaf/rake/db'
4
+ require 'shaf/rake/test'
5
+
@@ -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.instance_eval { @usage }
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.instance_eval { @identifiers }
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.instance_eval { @identifiers }&.size || 0
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
- define_singleton_method(method) do
22
- @settings.dig(env.to_s, method.to_s)
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
@@ -1,4 +1,7 @@
1
- require 'shaf/settings'
2
- require 'shaf/tasks/db'
3
- require 'shaf/tasks/test'
1
+ require 'shaf/tasks/test_task'
2
+ require 'shaf/tasks/db_task'
3
+ require 'shaf/tasks/api_doc_task'
4
4
 
5
+ module Shaf
6
+ include Tasks
7
+ end