shaf 0.5.2 → 0.6.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.
- 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
|