shaf 0.5.2 → 0.6.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/lib/shaf/formable/builder.rb +61 -0
- data/lib/shaf/formable/field.rb +60 -0
- data/lib/shaf/formable/form.rb +98 -0
- data/lib/shaf/formable.rb +11 -195
- data/lib/shaf/generator/migration/base.rb +6 -7
- data/lib/shaf/generator/migration.rb +1 -1
- data/lib/shaf/generator/templates/api/model.rb.erb +2 -1
- data/lib/shaf/helpers/paginate.rb +1 -1
- data/lib/shaf/immutable_attr.rb +51 -0
- data/lib/shaf/spec/payload_utils.rb +2 -0
- data/lib/shaf/upgrade/manifest.rb +15 -8
- data/lib/shaf/upgrade/package.rb +35 -9
- data/lib/shaf/version.rb +1 -1
- data/upgrades/0.6.0.tar.gz +0 -0
- data.tar.gz.sig +0 -0
- metadata +7 -2
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5fa1ac79ab7ea9af45c8d160116ab1aee7ed964254927fe9045664930405e2c6
|
4
|
+
data.tar.gz: bf9dc19468e7b25d5e0c94c8484558f89bd6ec84e47d4d00211c3d57029909fc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d383a3b23dd5675fc3683a8b2c76407bcc9cf25ff17eb699b4cbd75898d053fdd1d1a2b8fb522eedf4fe8c7330a726dfc126d39508ce7c50c0a211b0ba6e8a86
|
7
|
+
data.tar.gz: f03870ee03fdc20f04541c9bff9cde8a1d2fa1ef3b1a0a0a52b0a20724cee752a5abe3b6f5eb5ec330125c010f8bc1f85acd9e18eaa3f97884ff33d66fe087ea
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'shaf/formable/form'
|
2
|
+
|
3
|
+
module Shaf
|
4
|
+
module Formable
|
5
|
+
class Builder
|
6
|
+
DELEGATES = %i[title name action method type fields].freeze
|
7
|
+
|
8
|
+
attr_reader :forms
|
9
|
+
|
10
|
+
def initialize(&block)
|
11
|
+
@forms = []
|
12
|
+
@instance_accessors = []
|
13
|
+
|
14
|
+
exec_with_form(block)
|
15
|
+
end
|
16
|
+
|
17
|
+
def instance_accessor_for?(form)
|
18
|
+
@instance_accessors.include? form
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
attr_reader :form
|
24
|
+
|
25
|
+
def exec_with_form(block, action: nil)
|
26
|
+
current, @form = form, new_form
|
27
|
+
form.action = action if action
|
28
|
+
instance_exec(&block)
|
29
|
+
ensure
|
30
|
+
@form = current
|
31
|
+
end
|
32
|
+
|
33
|
+
def new_form
|
34
|
+
(form&.dup || Formable::Form.new).tap { |f| @forms << f }
|
35
|
+
end
|
36
|
+
|
37
|
+
def instance_accessor
|
38
|
+
@instance_accessors << form
|
39
|
+
end
|
40
|
+
|
41
|
+
DELEGATES.each do |name|
|
42
|
+
define_method(name) do |arg|
|
43
|
+
form.send("#{name}=".to_sym, arg)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def field(name, opts = {})
|
48
|
+
form.add_field(name, opts)
|
49
|
+
end
|
50
|
+
|
51
|
+
def method_missing(method, *args, &block)
|
52
|
+
return super unless args.empty? && block
|
53
|
+
exec_with_form(block, action: method)
|
54
|
+
end
|
55
|
+
|
56
|
+
def respond_to_missing?(*)
|
57
|
+
true
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'shaf/immutable_attr'
|
2
|
+
|
3
|
+
module Shaf
|
4
|
+
module Formable
|
5
|
+
class Field
|
6
|
+
extend Shaf::ImmutableAttr
|
7
|
+
|
8
|
+
immutable_reader :name, :type, :value, :label, :required
|
9
|
+
|
10
|
+
HTML_TYPE_MAPPINGS = {
|
11
|
+
'string' => 'text',
|
12
|
+
'boolean' => 'checkbox',
|
13
|
+
}.freeze
|
14
|
+
|
15
|
+
def initialize(name, params = {})
|
16
|
+
@name = name
|
17
|
+
@type = params[:type]
|
18
|
+
@label = params[:label]
|
19
|
+
@has_value = params.key? :value
|
20
|
+
@value = params[:value]
|
21
|
+
@required = params[:required] || false
|
22
|
+
end
|
23
|
+
|
24
|
+
def has_value?
|
25
|
+
@has_value
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_html
|
29
|
+
[
|
30
|
+
'<div class="form--input-group">',
|
31
|
+
label_element,
|
32
|
+
input_element,
|
33
|
+
'</div>'
|
34
|
+
].compact.join("\n")
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def label_element
|
40
|
+
str = (label || name || "").to_s
|
41
|
+
%Q(<label for="#{name}" class="form--label">#{str}</label>)
|
42
|
+
end
|
43
|
+
|
44
|
+
def input_element
|
45
|
+
_value = value ? %Q(value="#{value.to_s}") : nil
|
46
|
+
_required = required ? "required" : nil
|
47
|
+
attributes = [
|
48
|
+
%Q(type="#{HTML_TYPE_MAPPINGS[type.to_s]}"),
|
49
|
+
'class="form--input"',
|
50
|
+
%Q(id="#{name.to_s}"),
|
51
|
+
%Q(name="#{name.to_s}"),
|
52
|
+
]
|
53
|
+
attributes << %Q(value="#{value.to_s}") if value
|
54
|
+
attributes << "required" if required
|
55
|
+
|
56
|
+
"<input #{attributes.join(" ")}>"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'shaf/formable/field'
|
2
|
+
require 'shaf/immutable_attr'
|
3
|
+
|
4
|
+
module Shaf
|
5
|
+
module Formable
|
6
|
+
class Form
|
7
|
+
extend Shaf::ImmutableAttr
|
8
|
+
|
9
|
+
DEFAULT_TYPE = 'application/json'.freeze
|
10
|
+
|
11
|
+
attr_accessor :resource
|
12
|
+
immutable_accessor :title, :name, :href, :type, :self_link
|
13
|
+
immutable_reader :fields, :action
|
14
|
+
|
15
|
+
def initialize(params = {})
|
16
|
+
@title = params[:title]
|
17
|
+
@action = params[:action]
|
18
|
+
@name = params[:name] || name_from(@action)
|
19
|
+
@method = params[:method] ||= http_method_from(@action)
|
20
|
+
@type = params[:type] || DEFAULT_TYPE
|
21
|
+
@fields = (params[:fields] || {}).map do |name, args|
|
22
|
+
Field.new(name, args)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def method=(http_method)
|
27
|
+
@method = http_method.to_s.upcase
|
28
|
+
end
|
29
|
+
|
30
|
+
def method
|
31
|
+
return unless @method
|
32
|
+
@method.to_s.upcase
|
33
|
+
end
|
34
|
+
|
35
|
+
def fields=(fields)
|
36
|
+
@fields = fields.map { |name, args| Field.new(name, args) }
|
37
|
+
end
|
38
|
+
|
39
|
+
def action=(action)
|
40
|
+
@action = action
|
41
|
+
@name ||= name_from action
|
42
|
+
@method ||= http_method_from action
|
43
|
+
end
|
44
|
+
|
45
|
+
def add_field(name, opts)
|
46
|
+
@fields << Field.new(name, opts)
|
47
|
+
end
|
48
|
+
|
49
|
+
def dup
|
50
|
+
super.tap do |obj|
|
51
|
+
obj.instance_variable_set(:@fields, @fields.map(&:dup))
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def clone
|
56
|
+
dup.tap { |obj| obj.freeze if frozen? }
|
57
|
+
end
|
58
|
+
|
59
|
+
def to_html
|
60
|
+
form_element do
|
61
|
+
[
|
62
|
+
hidden_method_element,
|
63
|
+
fields.map(&:to_html).join("\n"),
|
64
|
+
submit_element
|
65
|
+
].compact.join("\n")
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def name_from(action)
|
72
|
+
:"#{action}-form" if action
|
73
|
+
end
|
74
|
+
|
75
|
+
def http_method_from(action)
|
76
|
+
return unless action
|
77
|
+
action&.to_sym == :edit ? 'PUT' : 'POST'
|
78
|
+
end
|
79
|
+
|
80
|
+
def form_element
|
81
|
+
[
|
82
|
+
%Q(<form class="form" method=#{method == 'GET' ? 'GET' : 'POST'}#{href ? %Q( action="#{href.to_s}") : ''}>),
|
83
|
+
block_given? ? yield : nil,
|
84
|
+
'</form>'
|
85
|
+
].compact.join("\n")
|
86
|
+
end
|
87
|
+
|
88
|
+
def hidden_method_element
|
89
|
+
return if %w[GET POST].include? method
|
90
|
+
%Q(<input type="hidden" name="_method" value="#{method}">)
|
91
|
+
end
|
92
|
+
|
93
|
+
def submit_element
|
94
|
+
%Q(<div class="form--input-group"><input type="submit" class="button" value="Submit"</div>)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
data/lib/shaf/formable.rb
CHANGED
@@ -1,204 +1,20 @@
|
|
1
|
+
require 'shaf/formable/builder'
|
2
|
+
|
1
3
|
module Shaf
|
2
4
|
module Formable
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
'boolean' => 'checkbox',
|
9
|
-
}.freeze
|
10
|
-
|
11
|
-
def initialize(name, params = {})
|
12
|
-
@name = name
|
13
|
-
@type = params[:type]
|
14
|
-
@label = params[:label]
|
15
|
-
@has_value = params.key? :value
|
16
|
-
@value = params[:value]
|
17
|
-
@required = params[:required] || false
|
18
|
-
end
|
19
|
-
|
20
|
-
def has_value?
|
21
|
-
@has_value
|
22
|
-
end
|
23
|
-
|
24
|
-
def to_html
|
25
|
-
[
|
26
|
-
'<div class="form--input-group">',
|
27
|
-
label_element,
|
28
|
-
input_element,
|
29
|
-
'</div>'
|
30
|
-
].compact.join("\n")
|
31
|
-
end
|
32
|
-
|
33
|
-
private
|
34
|
-
|
35
|
-
def label_element
|
36
|
-
str = (label || name || "").to_s
|
37
|
-
%Q(<label for="#{name}" class="form--label">#{str}</label>)
|
38
|
-
end
|
39
|
-
|
40
|
-
def input_element
|
41
|
-
_value = value ? %Q(value="#{value.to_s}") : nil
|
42
|
-
_required = required ? "required" : nil
|
43
|
-
attributes = [
|
44
|
-
%Q(type="#{HTML_TYPE_MAPPINGS[type.to_s]}"),
|
45
|
-
'class="form--input"',
|
46
|
-
%Q(id="#{name.to_s}"),
|
47
|
-
%Q(name="#{name.to_s}"),
|
48
|
-
]
|
49
|
-
attributes << %Q(value="#{value.to_s}") if value
|
50
|
-
attributes << "required" if required
|
51
|
-
|
52
|
-
"<input #{attributes.join(" ")}>"
|
53
|
-
end
|
54
|
-
end
|
5
|
+
def form(&block)
|
6
|
+
builder = Formable::Builder.new(&block)
|
7
|
+
builder.forms.each do |f|
|
8
|
+
next unless f.action
|
9
|
+
getter = "#{f.action}_form"
|
55
10
|
|
56
|
-
|
57
|
-
|
58
|
-
DEFAULT_TYPE = 'application/json'
|
59
|
-
|
60
|
-
attr_accessor :resource, :name, :title, :href, :type, :self_link
|
61
|
-
attr_reader :fields
|
62
|
-
|
63
|
-
def initialize(params = {})
|
64
|
-
@name = params[:name]
|
65
|
-
@title = params[:title]
|
66
|
-
@method = params[:method] || 'POST'
|
67
|
-
@type = params[:type] || DEFAULT_TYPE
|
68
|
-
@fields = (params[:fields] || {}).map { |name, args| Field.new(name, args) }
|
69
|
-
end
|
11
|
+
define_singleton_method(getter) { f }
|
12
|
+
next unless builder.instance_accessor_for? f
|
70
13
|
|
71
|
-
|
72
|
-
|
73
|
-
end
|
74
|
-
|
75
|
-
def method
|
76
|
-
@method.to_s.upcase
|
77
|
-
end
|
78
|
-
|
79
|
-
def fields=(fields)
|
80
|
-
@fields = fields.map { |name, args| Field.new(name, args) }
|
81
|
-
end
|
82
|
-
|
83
|
-
def add_field(name, opts)
|
84
|
-
@fields << Field.new(name, opts)
|
85
|
-
end
|
86
|
-
|
87
|
-
def to_html
|
88
|
-
form_element do
|
89
|
-
[
|
90
|
-
hidden_method_element,
|
91
|
-
fields.map { |f| f.to_html }.join("\n"),
|
92
|
-
submit_element,
|
93
|
-
].compact.join("\n")
|
14
|
+
define_method(getter) do
|
15
|
+
f.dup.tap { |fm| fm.resource = self }
|
94
16
|
end
|
95
17
|
end
|
96
|
-
|
97
|
-
private
|
98
|
-
|
99
|
-
def form_element
|
100
|
-
[
|
101
|
-
%Q(<form class="form" method=#{method == 'GET' ? 'GET' : 'POST'}#{href ? %Q( action="#{href.to_s}") : ''}>),
|
102
|
-
block_given? ? yield : nil,
|
103
|
-
"</form>",
|
104
|
-
].compact.join("\n")
|
105
|
-
end
|
106
|
-
|
107
|
-
def hidden_method_element
|
108
|
-
return if ['GET', 'POST'].include?(method)
|
109
|
-
%Q(<input type="hidden" name="_method" value="#{method}">)
|
110
|
-
end
|
111
|
-
|
112
|
-
def submit_element
|
113
|
-
%Q(<div class="form--input-group"><input type="submit" class="button" value="Submit"</div>)
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
|
-
class Builder
|
118
|
-
def self.call(block)
|
119
|
-
[
|
120
|
-
new(block, create: true).call,
|
121
|
-
new(block, edit: true).call
|
122
|
-
]
|
123
|
-
end
|
124
|
-
|
125
|
-
attr_reader :block
|
126
|
-
|
127
|
-
def initialize(block, create: false, edit: false)
|
128
|
-
@block = block
|
129
|
-
@create = create
|
130
|
-
@edit = edit
|
131
|
-
@form = nil
|
132
|
-
@default_method = @create ? :post : :put
|
133
|
-
end
|
134
|
-
|
135
|
-
def call
|
136
|
-
instance_exec(&block)
|
137
|
-
@form
|
138
|
-
end
|
139
|
-
|
140
|
-
def form
|
141
|
-
@form ||= Form.new(method: @default_method)
|
142
|
-
end
|
143
|
-
|
144
|
-
def name(name)
|
145
|
-
form.name = name
|
146
|
-
end
|
147
|
-
|
148
|
-
def title(title)
|
149
|
-
form.title = title
|
150
|
-
end
|
151
|
-
|
152
|
-
def method(method)
|
153
|
-
form.method = method
|
154
|
-
end
|
155
|
-
|
156
|
-
def type(type)
|
157
|
-
form.type = type
|
158
|
-
end
|
159
|
-
|
160
|
-
def fields(fields)
|
161
|
-
form.fields = fields
|
162
|
-
end
|
163
|
-
|
164
|
-
def field(name, opts = {})
|
165
|
-
form.add_field(name, opts)
|
166
|
-
end
|
167
|
-
|
168
|
-
def create(&b)
|
169
|
-
return unless @create
|
170
|
-
call_nested_block(b)
|
171
|
-
end
|
172
|
-
|
173
|
-
def edit(&b)
|
174
|
-
return unless @edit
|
175
|
-
call_nested_block(b)
|
176
|
-
end
|
177
|
-
|
178
|
-
def call_nested_block(b)
|
179
|
-
old, @block = @block, b
|
180
|
-
call
|
181
|
-
ensure
|
182
|
-
@block = old
|
183
|
-
end
|
184
|
-
end
|
185
|
-
|
186
|
-
module ClassMethods
|
187
|
-
attr_reader :create_form, :edit_form
|
188
|
-
|
189
|
-
def form(&block)
|
190
|
-
@create_form, @edit_form = Builder.(block)
|
191
|
-
end
|
192
|
-
end
|
193
|
-
|
194
|
-
def self.included(base)
|
195
|
-
base.extend(ClassMethods)
|
196
|
-
end
|
197
|
-
|
198
|
-
def edit_form
|
199
|
-
self.class.edit_form.tap do |form|
|
200
|
-
form&.resource = self
|
201
|
-
end
|
202
18
|
end
|
203
19
|
end
|
204
20
|
end
|
@@ -99,14 +99,13 @@ module Shaf
|
|
99
99
|
end
|
100
100
|
|
101
101
|
def regexp_db_format_string(type)
|
102
|
-
COMPLEX_DB_TYPES.
|
102
|
+
COMPLEX_DB_TYPES.find do |complex_type|
|
103
103
|
match = complex_type[:pattern].match(type) or next
|
104
104
|
validator = complex_type[:validator]
|
105
105
|
errors = validator&.call(type, match)
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
raise "Failed to process '#{type}': #{errors.join(', ')}"
|
106
|
+
raise "Failed to process '#{type}': #{errors&.join(', ')}" unless Array(errors).empty?
|
107
|
+
|
108
|
+
break complex_type[:strings].map { |s| replace_backreferences(match, s) }
|
110
109
|
end
|
111
110
|
end
|
112
111
|
|
@@ -118,13 +117,13 @@ module Shaf
|
|
118
117
|
end
|
119
118
|
|
120
119
|
def render
|
121
|
-
<<~
|
120
|
+
<<~RUBY
|
122
121
|
Sequel.migration do
|
123
122
|
change do
|
124
123
|
#{@changes.flatten.join("\n ")}
|
125
124
|
end
|
126
125
|
end
|
127
|
-
|
126
|
+
RUBY
|
128
127
|
end
|
129
128
|
end
|
130
129
|
|
@@ -1,5 +1,5 @@
|
|
1
1
|
class <%= class_name %> < Sequel::Model
|
2
|
-
|
2
|
+
extend Shaf::Formable
|
3
3
|
|
4
4
|
<% if form_fields.any? %>
|
5
5
|
form do
|
@@ -11,6 +11,7 @@ class <%= class_name %> < Sequel::Model
|
|
11
11
|
end
|
12
12
|
|
13
13
|
edit do
|
14
|
+
instance_accessor
|
14
15
|
title 'Update <%= class_name %>'
|
15
16
|
name 'update-<%= model_name %>'
|
16
17
|
end
|
@@ -8,7 +8,7 @@ module Shaf
|
|
8
8
|
|
9
9
|
def paginate!(collection, per_page = PAGINATION_PER_PAGE)
|
10
10
|
unless collection.respond_to? :paginate
|
11
|
-
log.
|
11
|
+
log.warn "Trying to paginate a collection that doesn't " \
|
12
12
|
"support pagination: #{collection}"
|
13
13
|
return
|
14
14
|
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Shaf
|
2
|
+
module ImmutableAttr
|
3
|
+
NON_DUPABLE_CLASSES = [
|
4
|
+
NilClass,
|
5
|
+
TrueClass,
|
6
|
+
FalseClass,
|
7
|
+
Symbol
|
8
|
+
]
|
9
|
+
|
10
|
+
def self.dup(obj)
|
11
|
+
return obj unless obj.respond_to? :dup
|
12
|
+
return obj if NON_DUPABLE_CLASSES.include? obj.class
|
13
|
+
obj.dup
|
14
|
+
end
|
15
|
+
|
16
|
+
def immutable_reader(*methods)
|
17
|
+
methods.each do |method|
|
18
|
+
define_method(method) do
|
19
|
+
value = instance_variable_get(:"@#{method}")
|
20
|
+
ImmutableAttr.dup(value)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def immutable_writer(*methods)
|
26
|
+
methods.each do |method|
|
27
|
+
define_method(:"#{method}=") do |value|
|
28
|
+
instance_variable_set(
|
29
|
+
:"@#{method}",
|
30
|
+
ImmutableAttr.dup(value)
|
31
|
+
)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def immutable_accessor(*methods)
|
37
|
+
methods.each do |method|
|
38
|
+
define_method(method) do
|
39
|
+
value = instance_variable_get(:"@#{method}")
|
40
|
+
ImmutableAttr.dup(value)
|
41
|
+
end
|
42
|
+
define_method(:"#{method}=") do |value|
|
43
|
+
instance_variable_set(
|
44
|
+
:"@#{method}",
|
45
|
+
ImmutableAttr.dup(value)
|
46
|
+
)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -5,17 +5,21 @@ module Shaf
|
|
5
5
|
class Manifest
|
6
6
|
attr_reader :target_version, :files
|
7
7
|
|
8
|
-
def initialize(
|
9
|
-
@target_version = target_version
|
8
|
+
def initialize(**params)
|
9
|
+
@target_version = params[:target_version]
|
10
10
|
@files = {}
|
11
|
-
@files[:patch] = build_patterns(patches)
|
12
|
-
@files[:add] = add || {}
|
13
|
-
@files[:drop] = (drop || []).map { |d| Regexp.new(d) }
|
11
|
+
@files[:patch] = build_patterns(params[:patches])
|
12
|
+
@files[:add] = params[:add] || {}
|
13
|
+
@files[:drop] = (params[:drop] || []).map { |d| Regexp.new(d) }
|
14
|
+
@files[:regexp] = build_patterns(params[:substitutes])
|
14
15
|
end
|
15
16
|
|
16
17
|
def patch_for(file)
|
17
|
-
|
18
|
-
|
18
|
+
files[:patch].select { |_, pattern| file =~ pattern }.keys
|
19
|
+
end
|
20
|
+
|
21
|
+
def regexp_for(file)
|
22
|
+
files[:regexp].select { |_, pattern| file =~ pattern }.keys
|
19
23
|
end
|
20
24
|
|
21
25
|
def drop?(file)
|
@@ -25,7 +29,8 @@ module Shaf
|
|
25
29
|
def stats
|
26
30
|
"Add: #{files[:add].size}, " \
|
27
31
|
"Del: #{files[:drop].size}, " \
|
28
|
-
"Patch: #{files[:patch].size}"
|
32
|
+
"Patch: #{files[:patch].size}, " \
|
33
|
+
"Regexp: #{files[:regexp].size}"
|
29
34
|
end
|
30
35
|
|
31
36
|
private
|
@@ -50,3 +55,5 @@ end
|
|
50
55
|
# 8ece24b8c440675bd3d188155909431c: api/policies/base_policy.rb
|
51
56
|
# drop:
|
52
57
|
# - api/policies/base_policy.rb
|
58
|
+
# substitutes:
|
59
|
+
# d3b07384d113edec49eaa6238ad5ff00: api/models/.*.rb
|
data/lib/shaf/upgrade/package.rb
CHANGED
@@ -4,6 +4,8 @@ require 'set'
|
|
4
4
|
require 'digest'
|
5
5
|
require 'open3'
|
6
6
|
require 'fileutils'
|
7
|
+
require 'tempfile'
|
8
|
+
require 'yaml'
|
7
9
|
|
8
10
|
module Shaf
|
9
11
|
module Upgrade
|
@@ -15,7 +17,6 @@ module Shaf
|
|
15
17
|
class VersionConflictError < UpgradeError; end
|
16
18
|
class ManifestNotFoundError < UpgradeError; end
|
17
19
|
class MissingFileError < UpgradeError; end
|
18
|
-
class FileNotInManifestError < UpgradeError; end
|
19
20
|
class BadChecksumError < UpgradeError; end
|
20
21
|
|
21
22
|
UPGRADE_FILES_PATH = File.join(Utils.gem_root, 'upgrades').freeze
|
@@ -78,6 +79,7 @@ module Shaf
|
|
78
79
|
apply_patches(dir)
|
79
80
|
apply_additions
|
80
81
|
apply_drops(dir)
|
82
|
+
apply_substitutes(dir)
|
81
83
|
end
|
82
84
|
|
83
85
|
def to_s
|
@@ -111,7 +113,8 @@ module Shaf
|
|
111
113
|
target_version: hash["target_version"],
|
112
114
|
patches: hash["patches"],
|
113
115
|
add: hash["add"],
|
114
|
-
drop: hash["drop"]
|
116
|
+
drop: hash["drop"],
|
117
|
+
substitutes: hash["substitutes"]
|
115
118
|
)
|
116
119
|
end
|
117
120
|
|
@@ -124,12 +127,9 @@ module Shaf
|
|
124
127
|
manifest_patches = @manifest.files[:patch].keys.to_set
|
125
128
|
raise MissingFileError if from_file < manifest_patches
|
126
129
|
|
127
|
-
|
128
130
|
to_add = @manifest.files[:add].keys.to_set
|
129
131
|
raise MissingFileError if from_file < to_add
|
130
132
|
|
131
|
-
raise FileNotInManifestError if (manifest_patches + to_add) < from_file
|
132
|
-
|
133
133
|
@files.each do |md5, content|
|
134
134
|
raise BadChecksumError unless Digest::MD5.hexdigest(content) == md5
|
135
135
|
end
|
@@ -144,10 +144,10 @@ module Shaf
|
|
144
144
|
|
145
145
|
def apply_patches(dir = nil)
|
146
146
|
files_in(dir).all? do |file|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
147
|
+
@manifest.patch_for(file).all? do |name|
|
148
|
+
patch = @files[name]
|
149
|
+
apply_patch(file, patch)
|
150
|
+
end
|
151
151
|
end
|
152
152
|
end
|
153
153
|
|
@@ -177,6 +177,32 @@ module Shaf
|
|
177
177
|
File.unlink(file) if @manifest.drop?(file)
|
178
178
|
end
|
179
179
|
end
|
180
|
+
|
181
|
+
def apply_substitutes(dir = nil)
|
182
|
+
files_in(dir).all? do |file|
|
183
|
+
@manifest.regexp_for(file).all? do |name|
|
184
|
+
params = symbolize_keys(YAML.safe_load(@files[name]))
|
185
|
+
apply_substitute(file, params)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
def apply_substitute(file, params)
|
191
|
+
pattern = params[:pattern] or return
|
192
|
+
replacement = params[:replace] or return
|
193
|
+
tmp = Tempfile.open do |new_file|
|
194
|
+
File.readlines(file).each do |line|
|
195
|
+
new_file << line.sub(Regexp.new(pattern), replacement)
|
196
|
+
end
|
197
|
+
new_file
|
198
|
+
end
|
199
|
+
FileUtils.mv(tmp.path, file)
|
200
|
+
end
|
201
|
+
|
202
|
+
# Refactor this when support for ruby < 2.5 is dropped
|
203
|
+
def symbolize_keys(hash)
|
204
|
+
hash.each_with_object({}) { |(k, v), h| h[k.to_sym] = v }
|
205
|
+
end
|
180
206
|
end
|
181
207
|
end
|
182
208
|
end
|
data/lib/shaf/version.rb
CHANGED
Binary file
|
data.tar.gz.sig
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: shaf
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sammy Henningsson
|
@@ -30,7 +30,7 @@ cert_chain:
|
|
30
30
|
ZMhjYR7sRczGJx+GxGU2EaR0bjRsPVlC4ywtFxoOfRG3WaJcpWGEoAoMJX6Z0bRv
|
31
31
|
M40=
|
32
32
|
-----END CERTIFICATE-----
|
33
|
-
date: 2018-
|
33
|
+
date: 2018-11-14 00:00:00.000000000 Z
|
34
34
|
dependencies:
|
35
35
|
- !ruby/object:Gem::Dependency
|
36
36
|
name: rake
|
@@ -137,6 +137,9 @@ files:
|
|
137
137
|
- lib/shaf/extensions/resource_uris.rb
|
138
138
|
- lib/shaf/extensions/symbolic_routes.rb
|
139
139
|
- lib/shaf/formable.rb
|
140
|
+
- lib/shaf/formable/builder.rb
|
141
|
+
- lib/shaf/formable/field.rb
|
142
|
+
- lib/shaf/formable/form.rb
|
140
143
|
- lib/shaf/generator.rb
|
141
144
|
- lib/shaf/generator/base.rb
|
142
145
|
- lib/shaf/generator/controller.rb
|
@@ -164,6 +167,7 @@ files:
|
|
164
167
|
- lib/shaf/helpers/json_html.rb
|
165
168
|
- lib/shaf/helpers/paginate.rb
|
166
169
|
- lib/shaf/helpers/payload.rb
|
170
|
+
- lib/shaf/immutable_attr.rb
|
167
171
|
- lib/shaf/middleware.rb
|
168
172
|
- lib/shaf/middleware/request_id.rb
|
169
173
|
- lib/shaf/rake.rb
|
@@ -222,6 +226,7 @@ files:
|
|
222
226
|
- templates/spec/spec_helper.rb
|
223
227
|
- upgrades/0.4.0.tar.gz
|
224
228
|
- upgrades/0.5.0.tar.gz
|
229
|
+
- upgrades/0.6.0.tar.gz
|
225
230
|
homepage: https://github.com/sammyhenningsson/shaf
|
226
231
|
licenses:
|
227
232
|
- MIT
|
metadata.gz.sig
CHANGED
Binary file
|