shaf 0.4.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data/lib/shaf/command/base.rb +51 -0
  4. data/lib/shaf/command/templates/Gemfile.erb +0 -1
  5. data/lib/shaf/command.rb +1 -47
  6. data/lib/shaf/extensions/resource_uris.rb +113 -151
  7. data/lib/shaf/generator/base.rb +61 -0
  8. data/lib/shaf/generator/migration/base.rb +133 -0
  9. data/lib/shaf/generator/migration/create_table.rb +4 -4
  10. data/lib/shaf/generator/migration.rb +1 -111
  11. data/lib/shaf/generator/serializer.rb +3 -51
  12. data/lib/shaf/generator/templates/api/controller.rb.erb +2 -0
  13. data/lib/shaf/generator/templates/api/policy.rb.erb +3 -2
  14. data/lib/shaf/generator/templates/api/serializer.rb.erb +2 -6
  15. data/lib/shaf/generator/templates/spec/integration_spec.rb.erb +30 -30
  16. data/lib/shaf/generator.rb +1 -57
  17. data/lib/shaf/helpers/cache_control.rb +21 -0
  18. data/lib/shaf/helpers.rb +4 -2
  19. data/lib/shaf/rake/db.rb +1 -1
  20. data/lib/shaf/upgrade/manifest.rb +29 -8
  21. data/lib/shaf/upgrade/package.rb +51 -27
  22. data/lib/shaf/version.rb +1 -1
  23. data/templates/api/controllers/docs_controller.rb +2 -0
  24. data/templates/api/controllers/root_controller.rb +2 -3
  25. data/templates/api/policies/base_policy.rb +3 -0
  26. data/templates/api/serializers/base_serializer.rb +4 -0
  27. data/templates/api/serializers/error_serializer.rb +3 -2
  28. data/templates/api/serializers/form_serializer.rb +2 -2
  29. data/templates/api/serializers/root_serializer.rb +3 -3
  30. data/templates/config/initializers/db_migrations.rb +24 -11
  31. data/templates/config/initializers/hal_presenter.rb +0 -5
  32. data/templates/config/settings.yml +8 -3
  33. data/upgrades/0.5.0.tar.gz +0 -0
  34. data.tar.gz.sig +0 -0
  35. metadata +69 -6
  36. metadata.gz.sig +0 -0
@@ -1,5 +1,3 @@
1
- require 'date'
2
-
3
1
  module Shaf
4
2
  module Generator
5
3
  module Migration
@@ -20,119 +18,11 @@ module Shaf
20
18
  raise Command::ArgumentError, e.message
21
19
  end
22
20
  end
23
-
24
- class Base
25
- DB_COL_TYPES = {
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'],
44
- }
45
-
46
- attr_reader :args
47
-
48
- class << self
49
- def inherited(child)
50
- Factory.register(child)
51
- end
52
-
53
- def identifier(*ids)
54
- @identifiers = ids.flatten.map(&:to_s)
55
- end
56
-
57
- def usage(str = nil, &block)
58
- @usage = str || block
59
- end
60
- end
61
-
62
- def initialize(*args)
63
- @args = args.dup
64
- end
65
-
66
- def call
67
- validate_args
68
- name = compile_migration_name
69
- compile_changes
70
- [target(name), render]
71
- rescue StandardError => e
72
- raise Command::ArgumentError, e.message
73
- end
74
-
75
- def add_change(change)
76
- @changes ||= []
77
- @changes << change if change
78
- end
79
-
80
- def db_type(type)
81
- type ||= :string
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
89
- end
90
-
91
- def column_def(str, create: true)
92
- name, type = str.split(':')
93
- format db_type(type)[create ? 0 : 1], name.downcase
94
- end
95
-
96
- def target(name)
97
- raise "Migration filename is nil" unless name
98
- "db/migrations/#{timestamp}_#{name}.rb"
99
- end
100
-
101
- private
102
-
103
- def timestamp
104
- DateTime.now.strftime("%Y%m%d%H%M%S")
105
- end
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
-
114
- def add_timestamp_columns?
115
- if File.exist? 'config/initializers/sequel.rb'
116
- require 'config/initializers/sequel'
117
- Sequel::Model.plugins.include? Sequel::Plugins::Timestamps
118
- end
119
- end
120
-
121
- def render
122
- <<~EOS
123
- Sequel.migration do
124
- change do
125
- #{@changes.flatten.join("\n ")}
126
- end
127
- end
128
- EOS
129
- end
130
- end
131
-
132
21
  end
133
22
  end
134
23
  end
135
24
 
25
+ require 'shaf/generator/migration/base'
136
26
  require 'shaf/generator/migration/add_column'
137
27
  require 'shaf/generator/migration/add_index'
138
28
  require 'shaf/generator/migration/create_table'
@@ -69,7 +69,7 @@ module Shaf
69
69
  end
70
70
 
71
71
  def links
72
- %w(doc:up self doc:create-form doc:edit-form doc:edit doc:delete)
72
+ %w(doc:up self doc:edit-form doc:delete)
73
73
  end
74
74
 
75
75
  def curies_with_doc
@@ -83,7 +83,7 @@ module Shaf
83
83
  # Example:
84
84
  # ```
85
85
  # curl -H "Accept: application/hal+json" \\
86
- # /doc/#{name}/rels/edit
86
+ # /doc/#{name}/rels/delete
87
87
  #```
88
88
  curie :doc do
89
89
  doc_curie_uri('#{name}')
@@ -96,9 +96,7 @@ module Shaf
96
96
  [
97
97
  collection_link,
98
98
  self_link,
99
- new_link,
100
99
  edit_link,
101
- update_link,
102
100
  delete_link,
103
101
  ]
104
102
  end
@@ -123,15 +121,6 @@ module Shaf
123
121
  )
124
122
  end
125
123
 
126
- def new_link
127
- link(
128
- rel: "doc:create-form",
129
- desc: "Link to a form to create a new #{name}",
130
- uri: "/#{plural_name}/form",
131
- uri_helper: "new_#{name}_uri"
132
- )
133
- end
134
-
135
124
  def edit_link
136
125
  link(
137
126
  rel: "doc:edit-form",
@@ -141,16 +130,6 @@ module Shaf
141
130
  )
142
131
  end
143
132
 
144
- def update_link
145
- link(
146
- rel: "doc:edit",
147
- desc: "Link to update this #{name}",
148
- method: "PUT",
149
- uri: "/#{plural_name}/5",
150
- uri_helper: "#{name}_uri(resource)"
151
- )
152
- end
153
-
154
133
  def delete_link
155
134
  link(
156
135
  rel: "doc:delete",
@@ -194,38 +173,13 @@ module Shaf
194
173
  EOS
195
174
  end
196
175
 
197
- def embeds
198
- [:'doc:edit-form']
199
- end
200
-
201
- def embeds_with_doc
202
- [
203
- <<~EOS.split("\n")
204
- # Auto generated doc:
205
- # A form to edit this #{name}
206
- embed :'doc:edit-form' do
207
- resource.edit_form.tap do |form|
208
- form.self_link = edit_#{name}_uri(resource)
209
- form.href = #{name}_uri(resource)
210
- end
211
- end
212
- EOS
213
- ]
214
- end
215
-
216
176
  def collection_with_doc
217
177
  <<~EOS.split("\n")
218
178
  collection of: '#{plural_name}' do
219
179
  link :self, #{plural_name}_uri
220
180
  link :up, root_uri
181
+ link :'doc:create-form', new_#{name}_uri
221
182
  curie(:doc) { doc_curie_uri('#{name}') }
222
-
223
- embed :'doc:create-form' do
224
- #{model_class_name}.create_form.tap do |form|
225
- form.self_link = new_#{name}_uri
226
- form.href = #{plural_name}_uri
227
- end
228
- end
229
183
  end
230
184
  EOS
231
185
  end
@@ -239,11 +193,9 @@ module Shaf
239
193
  policy_name: "#{name}_policy",
240
194
  attributes: attributes,
241
195
  links: links,
242
- embeds: embeds,
243
196
  attributes_with_doc: attributes_with_doc,
244
197
  curies_with_doc: curies_with_doc,
245
198
  links_with_doc: links_with_doc,
246
- embeds_with_doc: embeds_with_doc,
247
199
  collection_with_doc: collection_with_doc,
248
200
  }
249
201
  end
@@ -14,6 +14,7 @@ class <%= controller_class_name %> < BaseController
14
14
 
15
15
  get :new_<%= name %>_uri do
16
16
  authorize! :read
17
+ cache_control(:private, http_cache_max_age: :short)
17
18
  respond_with create_form
18
19
  end
19
20
 
@@ -31,6 +32,7 @@ class <%= controller_class_name %> < BaseController
31
32
 
32
33
  get :edit_<%= name %>_uri do
33
34
  authorize! :read
35
+ cache_control(:private, http_cache_max_age: :short)
34
36
  respond_with edit_form
35
37
  end
36
38
 
@@ -1,5 +1,6 @@
1
- class <%= policy_class_name %>
2
- include HALPresenter::Policy::DSL
1
+ require 'policies/base_policy'
2
+
3
+ class <%= policy_class_name %> < BasePolicy
3
4
 
4
5
  # Auto generated policy: Update this file to suite your API!
5
6
 
@@ -1,8 +1,7 @@
1
+ require 'serializers/base_serializer'
1
2
  require 'policies/<%= policy_name %>'
2
3
 
3
- class <%= class_name %>
4
- extend HALPresenter
5
- extend Shaf::UriHelper
4
+ class <%= class_name %> < BaseSerializer
6
5
 
7
6
  model <%= model_class_name %>
8
7
  policy <%= policy_class_name %>
@@ -15,9 +14,6 @@ class <%= class_name %>
15
14
  <% links_with_doc.each do |link| %>
16
15
  <%= link.join("\n ") %>
17
16
  <% end %>
18
- <% embeds_with_doc.each do |embed| %>
19
- <%= embed.join("\n ") %>
20
- <% end %>
21
17
  <% if collection_with_doc %>
22
18
  <%= collection_with_doc.join("\n ") %>
23
19
  <% end %>
@@ -34,21 +34,21 @@ describe <%= model_class_name %>, type: :integration do
34
34
  it "can create <%= plural_name %>" do
35
35
  get <%= plural_name %>_uri
36
36
 
37
- embedded :'doc:create-form' do
38
- links[:self][:href].must_equal new_<%= name %>_uri
39
- attributes[:href].must_equal <%= plural_name %>_uri
40
- attributes[:method].must_equal "POST"
41
- attributes[:name].must_equal "create-<%= name %>"
42
- attributes[:title].must_equal "Create <%= model_class_name %>"
43
- attributes[:type].must_equal "application/json"
44
- attributes[:fields].size.must_equal <%= params.size %>
45
-
46
- payload = fill_form attributes[:fields]
47
- post attributes[:href], payload
48
- status.must_equal 201
49
- link_rels.must_include(:self)
50
- headers["Location"].must_equal links[:self][:href]
51
- end
37
+ link_rels.must_include(:'doc:create-form')
38
+ follow_rel :'doc:create-form'
39
+ links[:self][:href].must_equal new_<%= name %>_uri
40
+ attributes[:href].must_equal <%= plural_name %>_uri
41
+ attributes[:method].must_equal "POST"
42
+ attributes[:name].must_equal "create-<%= name %>"
43
+ attributes[:title].must_equal "Create <%= model_class_name %>"
44
+ attributes[:type].must_equal "application/json"
45
+ attributes[:fields].size.must_equal <%= params.size %>
46
+
47
+ payload = fill_form attributes[:fields]
48
+ post attributes[:href], payload
49
+ status.must_equal 201
50
+ link_rels.must_include(:self)
51
+ headers["Location"].must_equal links[:self][:href]
52
52
 
53
53
  get <%= plural_name %>_uri
54
54
  status.must_equal 200
@@ -71,22 +71,22 @@ describe <%= model_class_name %>, type: :integration do
71
71
  <%= name %> = <%= model_class_name %>.create
72
72
  get <%= name %>_uri(<%= name %>)
73
73
  status.must_equal 200
74
- link_rels.must_include(:'doc:edit-form')
75
74
 
76
- embedded :'doc:edit-form' do
77
- links[:self][:href].must_equal edit_<%= name %>_uri(<%= name %>)
78
- attributes[:href].must_equal <%= name %>_uri(<%= name %>)
79
- attributes[:method].must_equal "PUT"
80
- attributes[:name].must_equal "update-<%= name %>"
81
- attributes[:title].must_equal "Update <%= model_class_name %>"
82
- attributes[:type].must_equal "application/json"
83
- attributes[:fields].size.must_equal <%= params.size %>
84
-
85
- payload = fill_form attributes[:fields]
86
- put attributes[:href], payload
87
- status.must_equal 200
88
- link_rels.must_include(:self)
89
- end
75
+ link_rels.must_include(:'doc:edit-form')
76
+ follow_rel :'doc:edit-form'
77
+
78
+ links[:self][:href].must_equal edit_<%= name %>_uri(<%= name %>)
79
+ attributes[:href].must_equal <%= name %>_uri(<%= name %>)
80
+ attributes[:method].must_equal "PUT"
81
+ attributes[:name].must_equal "update-<%= name %>"
82
+ attributes[:title].must_equal "Update <%= model_class_name %>"
83
+ attributes[:type].must_equal "application/json"
84
+ attributes[:fields].size.must_equal <%= params.size %>
85
+
86
+ payload = fill_form attributes[:fields]
87
+ put attributes[:href], payload
88
+ status.must_equal 200
89
+ link_rels.must_include(:self)
90
90
  end
91
91
 
92
92
  it "<%= plural_name %> can be deleted" do
@@ -1,6 +1,3 @@
1
- require 'fileutils'
2
- require 'erb'
3
- require 'ostruct'
4
1
  require 'shaf/registrable_factory'
5
2
 
6
3
  module Shaf
@@ -8,63 +5,10 @@ module Shaf
8
5
  class Factory
9
6
  extend RegistrableFactory
10
7
  end
11
-
12
- class Base
13
- attr_reader :args
14
-
15
- class << self
16
- def inherited(child)
17
- Factory.register(child)
18
- end
19
-
20
- def identifier(*ids)
21
- @identifiers = ids.flatten
22
- end
23
-
24
- def usage(str = nil, &block)
25
- @usage = str || block
26
- end
27
-
28
- def options(option_parser, options); end
29
- end
30
-
31
- def initialize(*args)
32
- @args = args.dup
33
- end
34
-
35
- def call(options = {}); end
36
-
37
- def template_dir
38
- File.expand_path('../generator/templates', __FILE__)
39
- end
40
-
41
- def read_template(file, directory = nil)
42
- directory ||= template_dir
43
- filename = File.join(directory, file)
44
- filename << ".erb" unless filename.end_with?(".erb")
45
- File.read(filename)
46
- end
47
-
48
- def render(template, locals = {})
49
- str = read_template(template)
50
- locals[:changes] ||= []
51
- b = OpenStruct.new(locals).instance_eval { binding }
52
- ERB.new(str, 0, '%-<>').result(b)
53
- rescue SystemCallError => e
54
- puts "Failed to render template #{template}: #{e.message}"
55
- raise
56
- end
57
-
58
- def write_output(file, content)
59
- dir = File.dirname(file)
60
- FileUtils.mkdir_p(dir) unless Dir.exist?(dir)
61
- File.write(file, content)
62
- puts "Added: #{file}"
63
- end
64
- end
65
8
  end
66
9
  end
67
10
 
11
+ require 'shaf/generator/base'
68
12
  require 'shaf/generator/controller'
69
13
  require 'shaf/generator/migration'
70
14
  require 'shaf/generator/model'
@@ -0,0 +1,21 @@
1
+ module Shaf
2
+ module CacheControl
3
+
4
+ def cache_control(*args, **kwargs)
5
+ return unless Shaf::Settings.http_cache
6
+ set_max_age(kwargs)
7
+ super(*args, **kwargs)
8
+ end
9
+
10
+ private
11
+
12
+ def set_max_age(kwargs)
13
+ max_age = kwargs[:http_cache_max_age] or return
14
+ if max_age.is_a? Symbol
15
+ key = "http_cache_max_age_#{max_age}".to_sym
16
+ max_age = Settings.respond_to?(key) ? Settings.send(key) : 86400
17
+ kwargs[:http_cache_max_age] = max_age
18
+ end
19
+ end
20
+ end
21
+ end
data/lib/shaf/helpers.rb CHANGED
@@ -1,13 +1,15 @@
1
- require 'shaf/helpers/payload'
1
+ require 'shaf/helpers/cache_control'
2
2
  require 'shaf/helpers/json_html'
3
3
  require 'shaf/helpers/paginate'
4
+ require 'shaf/helpers/payload'
4
5
 
5
6
  module Shaf
6
7
  def self.helpers
7
8
  [
8
- Payload,
9
+ CacheControl,
9
10
  JsonHtml,
10
11
  Paginate,
12
+ Payload,
11
13
  ]
12
14
  end
13
15
  end
data/lib/shaf/rake/db.rb CHANGED
@@ -73,7 +73,7 @@ Shaf::DbTask.new(:seed, description: "Seed the Database") do
73
73
 
74
74
  if Dir.exist? "db/seeds"
75
75
  Dir['db/seeds/**/*.rb'].each do |file|
76
- require file.sub(".rb", "")
76
+ require file.sub(/.rb\Z/, "")
77
77
  end
78
78
  end
79
79
  end
@@ -3,22 +3,39 @@ require 'yaml'
3
3
  module Shaf
4
4
  module Upgrade
5
5
  class Manifest
6
- attr_reader :target_version, :patches
6
+ attr_reader :target_version, :files
7
7
 
8
- def initialize(target_version:, patches: {})
8
+ def initialize(target_version:, patches: nil, add: nil, drop: nil)
9
9
  @target_version = target_version
10
- @patches = build_patterns(patches)
10
+ @files = {}
11
+ @files[:patch] = build_patterns(patches)
12
+ @files[:add] = add || {}
13
+ @files[:drop] = (drop || []).map { |d| Regexp.new(d) }
11
14
  end
12
15
 
16
+ def patch_for(file)
17
+ first_match = files[:patch].find { |_, pattern| file =~ pattern }
18
+ (first_match || []).first
19
+ end
20
+
21
+ def drop?(file)
22
+ files[:drop].any? { |pattern| file =~ pattern }
23
+ end
24
+
25
+ def stats
26
+ "Add: #{files[:add].size}, " \
27
+ "Del: #{files[:drop].size}, " \
28
+ "Patch: #{files[:patch].size}"
29
+ end
30
+
31
+ private
32
+
13
33
  def build_patterns(patches)
34
+ return {} unless patches
14
35
  patches.each_with_object({}) do |(chksum, pattern), hash|
15
- hash[chksum] = /#{pattern}/
36
+ hash[chksum] = Regexp.new(pattern)
16
37
  end
17
38
  end
18
-
19
- def patch_name_for(file)
20
- @patches.select { |_, pattern| pattern =~ file }.keys.first
21
- end
22
39
  end
23
40
  end
24
41
  end
@@ -29,3 +46,7 @@ end
29
46
  # patches:
30
47
  # cd5b0bf61070a9fd57e60c45e9aaf64a: config/database.rb
31
48
  # 59783ecfa5f41b84c6fad734e7aa6a1d: Rakefile
49
+ # add:
50
+ # 8ece24b8c440675bd3d188155909431c: api/policies/base_policy.rb
51
+ # drop:
52
+ # - api/policies/base_policy.rb
@@ -3,6 +3,7 @@ require 'zlib'
3
3
  require 'set'
4
4
  require 'digest'
5
5
  require 'open3'
6
+ require 'fileutils'
6
7
 
7
8
  module Shaf
8
9
  module Upgrade
@@ -13,8 +14,8 @@ module Shaf
13
14
  class VersionNotFoundError < UpgradeError; end
14
15
  class VersionConflictError < UpgradeError; end
15
16
  class ManifestNotFoundError < UpgradeError; end
16
- class MissingPatchError < UpgradeError; end
17
- class PatchNotInManifestError < UpgradeError; end
17
+ class MissingFileError < UpgradeError; end
18
+ class FileNotInManifestError < UpgradeError; end
18
19
  class BadChecksumError < UpgradeError; end
19
20
 
20
21
  UPGRADE_FILES_PATH = File.join(Utils.gem_root, 'upgrades').freeze
@@ -50,10 +51,10 @@ module Shaf
50
51
  end
51
52
  end
52
53
 
53
- def initialize(version)
54
+ def initialize(version, manifest = nil, files = {})
54
55
  @version = Version.new(version)
55
- @manifest = nil
56
- @patches = {}
56
+ @manifest = manifest
57
+ @files = files
57
58
  end
58
59
 
59
60
  def load
@@ -74,22 +75,15 @@ module Shaf
74
75
  end
75
76
 
76
77
  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
78
+ apply_patches(dir)
79
+ apply_additions
80
+ apply_drops(dir)
83
81
  end
84
82
 
85
83
  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
84
+ str = "Shaf::Upgrade::Package for version #{@version}"
85
+ return str if @manifest.nil?
86
+ "#{str} (#{@manifest.stats})"
93
87
  end
94
88
 
95
89
  private
@@ -107,15 +101,17 @@ module Shaf
107
101
  if filename == MANIFEST_FILENAME
108
102
  parse_manifest content
109
103
  else
110
- @patches[filename] = content
104
+ @files[filename] = content
111
105
  end
112
106
  end
113
107
 
114
108
  def parse_manifest(content)
115
- h = YAML.safe_load(content)
109
+ hash = YAML.safe_load(content)
116
110
  @manifest = Manifest.new(
117
- target_version: h['target_version'],
118
- patches: h['patches']
111
+ target_version: hash["target_version"],
112
+ patches: hash["patches"],
113
+ add: hash["add"],
114
+ drop: hash["drop"]
119
115
  )
120
116
  end
121
117
 
@@ -123,12 +119,18 @@ module Shaf
123
119
  raise ManifestNotFoundError unless @manifest
124
120
  raise VersionConflictError unless @version == @manifest.target_version
125
121
 
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
122
+ from_file = @files.keys.to_set
130
123
 
131
- @patches.each do |md5, content|
124
+ manifest_patches = @manifest.files[:patch].keys.to_set
125
+ raise MissingFileError if from_file < manifest_patches
126
+
127
+
128
+ to_add = @manifest.files[:add].keys.to_set
129
+ raise MissingFileError if from_file < to_add
130
+
131
+ raise FileNotInManifestError if (manifest_patches + to_add) < from_file
132
+
133
+ @files.each do |md5, content|
132
134
  raise BadChecksumError unless Digest::MD5.hexdigest(content) == md5
133
135
  end
134
136
 
@@ -140,6 +142,15 @@ module Shaf
140
142
  Dir["#{dir}**/*"]
141
143
  end
142
144
 
145
+ def apply_patches(dir = nil)
146
+ files_in(dir).all? do |file|
147
+ name = @manifest.patch_for(file) # returns nil when file
148
+ next true unless name # shouldn't be patched
149
+ patch = @files[name]
150
+ apply_patch(file, patch)
151
+ end
152
+ end
153
+
143
154
  def apply_patch(file, patch)
144
155
  success = nil
145
156
  Open3.popen3('patch', file, '-r', '-') do |i, o, e, t|
@@ -153,6 +164,19 @@ module Shaf
153
164
  success
154
165
  end
155
166
 
167
+ def apply_additions
168
+ @manifest.files[:add].each do |chksum, filename|
169
+ content = @files[chksum]
170
+ FileUtils.mkdir_p File.dirname(filename)
171
+ File.open(filename, 'w') { |file| file.write(content) }
172
+ end
173
+ end
174
+
175
+ def apply_drops(dir = nil)
176
+ files_in(dir).map do |file|
177
+ File.unlink(file) if @manifest.drop?(file)
178
+ end
179
+ end
156
180
  end
157
181
  end
158
182
  end
data/lib/shaf/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Shaf
2
- VERSION = "0.4.1"
2
+ VERSION = "0.5.0"
3
3
  end