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
@@ -1,8 +1,9 @@
|
|
1
1
|
require 'shaf/api_doc/document'
|
2
|
+
require 'shaf/api_doc/comment'
|
2
3
|
|
3
4
|
module Shaf
|
4
|
-
module
|
5
|
-
class
|
5
|
+
module Tasks
|
6
|
+
class ApiDocTask
|
6
7
|
include Rake::DSL
|
7
8
|
|
8
9
|
attr_accessor :document_class, :source_dir, :html_output_dir, :yaml_output_dir
|
@@ -10,7 +11,7 @@ module Shaf
|
|
10
11
|
def initialize
|
11
12
|
yield self if block_given?
|
12
13
|
validate_attributes!
|
13
|
-
@document_class ||= Document
|
14
|
+
@document_class ||= ApiDoc::Document
|
14
15
|
define_tasks
|
15
16
|
end
|
16
17
|
|
@@ -48,7 +49,7 @@ module Shaf
|
|
48
49
|
|
49
50
|
def read_file(file)
|
50
51
|
doc = document_class.new
|
51
|
-
comment = Comment.new
|
52
|
+
comment = ApiDoc::Comment.new
|
52
53
|
|
53
54
|
File.readlines(file).each do |line|
|
54
55
|
next if empty_line?(line)
|
@@ -59,7 +60,7 @@ module Shaf
|
|
59
60
|
end
|
60
61
|
|
61
62
|
parse_line(line, doc, comment)
|
62
|
-
comment = Comment.new
|
63
|
+
comment = ApiDoc::Comment.new
|
63
64
|
end
|
64
65
|
|
65
66
|
return doc unless block_given?
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Shaf
|
2
|
+
module Tasks
|
3
|
+
class DbTask
|
4
|
+
include Rake::DSL
|
5
|
+
|
6
|
+
def initialize(name, description:, args: [], &block)
|
7
|
+
@name = name
|
8
|
+
@desc = description
|
9
|
+
@args = args
|
10
|
+
@block = block
|
11
|
+
define_task
|
12
|
+
end
|
13
|
+
|
14
|
+
def define_task
|
15
|
+
namespace :db do
|
16
|
+
desc @desc
|
17
|
+
task @name, @args do |t, args|
|
18
|
+
require 'config/database'
|
19
|
+
Sequel.extension :migration
|
20
|
+
instance_exec(t, args, &@block)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def migrations
|
26
|
+
@migrations ||= DB[:schema_migrations].all
|
27
|
+
end
|
28
|
+
|
29
|
+
def last_migration
|
30
|
+
DB[:schema_migrations].order(:filename).last
|
31
|
+
end
|
32
|
+
|
33
|
+
def extract_version_and_filename(filename)
|
34
|
+
return [] unless filename
|
35
|
+
filename = filename[:filename] if filename.is_a? Hash
|
36
|
+
match = /(\d*)_(.*).rb/.match(filename)
|
37
|
+
return [] unless match
|
38
|
+
match[1..2]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'rake/testtask'
|
2
|
+
|
3
|
+
module Shaf
|
4
|
+
module Tasks
|
5
|
+
class TestTask < Rake::TestTask
|
6
|
+
def initialize(*args)
|
7
|
+
super(*args) do |t|
|
8
|
+
t.libs = %w(. api spec)
|
9
|
+
t.verbose = true
|
10
|
+
t.warning = false
|
11
|
+
yield self if block_given?
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/shaf/upgrade.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Shaf
|
4
|
+
module Upgrade
|
5
|
+
class Manifest
|
6
|
+
attr_reader :target_version, :patches
|
7
|
+
|
8
|
+
def initialize(target_version:, patches: {})
|
9
|
+
@target_version = target_version
|
10
|
+
@patches = build_patterns(patches)
|
11
|
+
end
|
12
|
+
|
13
|
+
def build_patterns(patches)
|
14
|
+
patches.each_with_object({}) do |(chksum, pattern), hash|
|
15
|
+
hash[chksum] = /#{pattern}/
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def patch_name_for(file)
|
20
|
+
@patches.select { |_, pattern| pattern =~ file }.keys.first
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Example of manifest:
|
27
|
+
# ---
|
28
|
+
# target_version: 0.4.0
|
29
|
+
# patches:
|
30
|
+
# cd5b0bf61070a9fd57e60c45e9aaf64a: config/database.rb
|
31
|
+
# 59783ecfa5f41b84c6fad734e7aa6a1d: Rakefile
|
@@ -0,0 +1,158 @@
|
|
1
|
+
require 'rubygems/package'
|
2
|
+
require 'zlib'
|
3
|
+
require 'set'
|
4
|
+
require 'digest'
|
5
|
+
require 'open3'
|
6
|
+
|
7
|
+
module Shaf
|
8
|
+
module Upgrade
|
9
|
+
class Package
|
10
|
+
include Comparable
|
11
|
+
|
12
|
+
class UpgradeError < StandardError; end
|
13
|
+
class VersionNotFoundError < UpgradeError; end
|
14
|
+
class VersionConflictError < UpgradeError; end
|
15
|
+
class ManifestNotFoundError < UpgradeError; end
|
16
|
+
class MissingPatchError < UpgradeError; end
|
17
|
+
class PatchNotInManifestError < UpgradeError; end
|
18
|
+
class BadChecksumError < UpgradeError; end
|
19
|
+
|
20
|
+
UPGRADE_FILES_PATH = File.join(Utils.gem_root, 'upgrades').freeze
|
21
|
+
MANIFEST_FILENAME = 'manifest'.freeze
|
22
|
+
|
23
|
+
attr_reader :version
|
24
|
+
|
25
|
+
class << self
|
26
|
+
def all
|
27
|
+
target_versions.map(&method(:new))
|
28
|
+
end
|
29
|
+
|
30
|
+
def load(version)
|
31
|
+
v = strip_suffix(version)
|
32
|
+
raise VersionNotFoundError unless target_versions.include? v
|
33
|
+
new(v).tap(&:load)
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def target_versions
|
39
|
+
return @target_versions if @target_versions
|
40
|
+
|
41
|
+
files = Dir[File.join(UPGRADE_FILES_PATH, '*.tar.gz')]
|
42
|
+
@target_versions = files.each_with_object([]) do |file, versions|
|
43
|
+
str = File.basename(file, '.tar.gz')
|
44
|
+
versions << Version.new(str)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def strip_suffix(file)
|
49
|
+
file.sub('.tar.gz', '')
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def initialize(version)
|
54
|
+
@version = Version.new(version)
|
55
|
+
@manifest = nil
|
56
|
+
@patches = {}
|
57
|
+
end
|
58
|
+
|
59
|
+
def load
|
60
|
+
File.open(tarball, 'rb') do |file|
|
61
|
+
Zlib::GzipReader.wrap(file) do |gz|
|
62
|
+
Gem::Package::TarReader.new(gz) do |tar|
|
63
|
+
tar.each(&method(:add_tar_entry))
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
validate
|
68
|
+
self
|
69
|
+
end
|
70
|
+
|
71
|
+
def <=>(other)
|
72
|
+
version = other.is_a?(String) ? other : other.version
|
73
|
+
@version <=> version
|
74
|
+
end
|
75
|
+
|
76
|
+
def apply(dir = nil)
|
77
|
+
files_in(dir).all? do |file|
|
78
|
+
name = @manifest.patch_name_for(file) # returns nil when file
|
79
|
+
next true unless name # shouldn't be patched
|
80
|
+
patch = @patches[name]
|
81
|
+
apply_patch(file, patch)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def to_s
|
86
|
+
if @manifest.nil?
|
87
|
+
"Shaf::Upgrade::Package for version #{@version}"
|
88
|
+
else
|
89
|
+
count = @patches.size
|
90
|
+
count_str = "#{count} patch#{count == 1 ? "" : "es"}"
|
91
|
+
"Shaf::Upgrade::Package for version #{@version}, containing #{count_str}"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def tarball
|
98
|
+
file = File.join(UPGRADE_FILES_PATH, "#{@version}.tar.gz")
|
99
|
+
return file if File.exist? file
|
100
|
+
raise VersionNotFoundError
|
101
|
+
end
|
102
|
+
|
103
|
+
def add_tar_entry(entry)
|
104
|
+
filename = entry.full_name
|
105
|
+
content = entry.read
|
106
|
+
|
107
|
+
if filename == MANIFEST_FILENAME
|
108
|
+
parse_manifest content
|
109
|
+
else
|
110
|
+
@patches[filename] = content
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def parse_manifest(content)
|
115
|
+
h = YAML.safe_load(content)
|
116
|
+
@manifest = Manifest.new(
|
117
|
+
target_version: h['target_version'],
|
118
|
+
patches: h['patches']
|
119
|
+
)
|
120
|
+
end
|
121
|
+
|
122
|
+
def validate
|
123
|
+
raise ManifestNotFoundError unless @manifest
|
124
|
+
raise VersionConflictError unless @version == @manifest.target_version
|
125
|
+
|
126
|
+
from_manifest = @manifest.patches.keys.to_set
|
127
|
+
from_file = @patches.keys.to_set
|
128
|
+
raise MissingPatchError if from_file < from_manifest
|
129
|
+
raise PatchNotInManifestError if from_manifest < from_file
|
130
|
+
|
131
|
+
@patches.each do |md5, content|
|
132
|
+
raise BadChecksumError unless Digest::MD5.hexdigest(content) == md5
|
133
|
+
end
|
134
|
+
|
135
|
+
true
|
136
|
+
end
|
137
|
+
|
138
|
+
def files_in(dir)
|
139
|
+
dir += '/' if !dir.nil? && dir[-1] != '/'
|
140
|
+
Dir["#{dir}**/*"]
|
141
|
+
end
|
142
|
+
|
143
|
+
def apply_patch(file, patch)
|
144
|
+
success = nil
|
145
|
+
Open3.popen3('patch', file, '-r', '-') do |i, o, e, t|
|
146
|
+
i.write patch
|
147
|
+
i.close
|
148
|
+
puts o.read
|
149
|
+
err = e.read
|
150
|
+
puts err unless err.empty?
|
151
|
+
success = t.value.success?
|
152
|
+
end
|
153
|
+
success
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Shaf
|
2
|
+
module Upgrade
|
3
|
+
class Version
|
4
|
+
include Comparable
|
5
|
+
|
6
|
+
attr_reader :major, :minor, :patch
|
7
|
+
|
8
|
+
alias eql? ==
|
9
|
+
|
10
|
+
class UpgradeVersionError < StandardError
|
11
|
+
def initialize(message = "")
|
12
|
+
super("Bad upgrade version: #{message}")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(version)
|
17
|
+
if self.class === version
|
18
|
+
@major, @minor, @patch = [:major, :minor, :patch].map { |m| version.send m }
|
19
|
+
else
|
20
|
+
raise UpgradeVersionError.new(version) unless version =~ /\d+\.\d+\.\d+/
|
21
|
+
@major, @minor, @patch = split_version(version)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def <=>(other)
|
26
|
+
if other.is_a? String
|
27
|
+
compare_version(*split_version(other))
|
28
|
+
else
|
29
|
+
compare_version(other.major, other.minor, other.patch)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_s
|
34
|
+
[major, minor, patch].join('.')
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def split_version(str)
|
40
|
+
str.split('.').map(&:to_i)
|
41
|
+
end
|
42
|
+
|
43
|
+
def compare_version(other_major, other_minor, other_patch)
|
44
|
+
[
|
45
|
+
major <=> other_major,
|
46
|
+
minor <=> other_minor,
|
47
|
+
patch <=> other_patch
|
48
|
+
].find { |x| !x.zero? } || 0
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/lib/shaf/utils.rb
CHANGED
@@ -2,6 +2,8 @@ module Shaf
|
|
2
2
|
module Utils
|
3
3
|
class ProjectRootNotFound < StandardError; end
|
4
4
|
|
5
|
+
SHAF_VERSION_FILE = '.shaf'.freeze
|
6
|
+
|
5
7
|
# FIXME!!!
|
6
8
|
def self.pluralize(noun)
|
7
9
|
noun + 's' # FIXME!!
|
@@ -11,6 +13,10 @@ module Shaf
|
|
11
13
|
name.capitalize.gsub(/[_-](\w)/) { $1.upcase }
|
12
14
|
end
|
13
15
|
|
16
|
+
def self.gem_root
|
17
|
+
File.expand_path('../../..', __FILE__)
|
18
|
+
end
|
19
|
+
|
14
20
|
# FIXME!!!
|
15
21
|
def self.singularize(noun)
|
16
22
|
return singularize(noun.to_s).to_sym if noun.is_a? Symbol
|
@@ -18,6 +24,10 @@ module Shaf
|
|
18
24
|
noun[0..-2]
|
19
25
|
end
|
20
26
|
|
27
|
+
def gem_root
|
28
|
+
self.class.gem_root
|
29
|
+
end
|
30
|
+
|
21
31
|
def project_root
|
22
32
|
dir = Dir.pwd
|
23
33
|
20.times do
|
@@ -61,5 +71,25 @@ module Shaf
|
|
61
71
|
def pluralize(noun)
|
62
72
|
Utils::pluralize(noun)
|
63
73
|
end
|
74
|
+
|
75
|
+
def read_shaf_file
|
76
|
+
return {} unless File.exist? SHAF_VERSION_FILE
|
77
|
+
str = File.read(SHAF_VERSION_FILE)
|
78
|
+
YAML.load(str) || {}
|
79
|
+
end
|
80
|
+
|
81
|
+
def write_shaf_file(data = {})
|
82
|
+
data = read_shaf_file.merge(data)
|
83
|
+
File.write SHAF_VERSION_FILE,
|
84
|
+
YAML.dump(data)
|
85
|
+
end
|
86
|
+
|
87
|
+
def read_shaf_version
|
88
|
+
read_shaf_file['version']
|
89
|
+
end
|
90
|
+
|
91
|
+
def write_shaf_version(version = nil)
|
92
|
+
write_shaf_file('version' => version || Shaf::VERSION)
|
93
|
+
end
|
64
94
|
end
|
65
95
|
end
|
data/lib/shaf/version.rb
CHANGED
data/templates/Rakefile
CHANGED
@@ -2,11 +2,10 @@ require 'rubygems'
|
|
2
2
|
require 'bundler'
|
3
3
|
Bundler.setup(:default, :development)
|
4
4
|
$:.unshift __dir__
|
5
|
-
require 'shaf/
|
5
|
+
require 'shaf/rake'
|
6
6
|
require 'shaf/settings'
|
7
|
-
require 'shaf/api_doc'
|
8
7
|
|
9
|
-
Shaf::
|
8
|
+
Shaf::ApiDocTask.new do |api_doc|
|
10
9
|
api_doc.source_dir = File.join(%w(api serializers))
|
11
10
|
api_doc.html_output_dir = File.join(Shaf::Settings.public_folder, "doc")
|
12
11
|
api_doc.yaml_output_dir = Shaf::Settings.documents_dir || "doc/api"
|
@@ -3,7 +3,7 @@ require 'sequel'
|
|
3
3
|
require 'config/constants'
|
4
4
|
require 'fileutils'
|
5
5
|
|
6
|
-
|
6
|
+
config = {
|
7
7
|
|
8
8
|
production: {
|
9
9
|
adapter: 'postgres',
|
@@ -28,7 +28,7 @@ CONFIG = {
|
|
28
28
|
|
29
29
|
env = Sinatra::Application.settings.environment
|
30
30
|
|
31
|
-
unless
|
31
|
+
unless config[env]
|
32
32
|
STDERR.puts "No Database config for environment '#{env}'"
|
33
33
|
exit 1
|
34
34
|
end
|
@@ -36,5 +36,5 @@ end
|
|
36
36
|
MIGRATIONS_DIR = File.join(APP_ROOT, Shaf::Settings.migrations_dir)
|
37
37
|
FileUtils.mkdir_p(MIGRATIONS_DIR) unless Dir.exist?(MIGRATIONS_DIR)
|
38
38
|
|
39
|
-
DB = Sequel.connect(
|
39
|
+
DB = Sequel.connect(config[env])
|
40
40
|
|