refile 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +56 -0
  3. data/.travis.yml +11 -0
  4. data/Gemfile +1 -1
  5. data/History.md +15 -0
  6. data/README.md +41 -5
  7. data/Rakefile +4 -1
  8. data/config/locales/en.yml +2 -0
  9. data/config/routes.rb +4 -2
  10. data/lib/refile/app.rb +96 -71
  11. data/lib/refile/attacher.rb +115 -0
  12. data/lib/refile/attachment/active_record.rb +5 -5
  13. data/lib/refile/attachment.rb +50 -117
  14. data/lib/refile/backend/file_system.rb +3 -3
  15. data/lib/refile/backend/s3.rb +7 -12
  16. data/lib/refile/file.rb +1 -3
  17. data/lib/refile/image_processing.rb +6 -3
  18. data/lib/refile/rails/attachment_helper.rb +18 -21
  19. data/lib/refile/rails.rb +2 -1
  20. data/lib/refile/random_hasher.rb +9 -8
  21. data/lib/refile/signature.rb +16 -0
  22. data/lib/refile/version.rb +1 -1
  23. data/lib/refile.rb +59 -1
  24. data/refile.gemspec +10 -6
  25. data/spec/refile/app_spec.rb +28 -4
  26. data/spec/refile/attachment_spec.rb +134 -33
  27. data/spec/refile/backend_examples.rb +7 -11
  28. data/spec/refile/features/direct_upload_spec.rb +0 -1
  29. data/spec/refile/features/normal_upload_spec.rb +21 -0
  30. data/spec/refile/features/presigned_upload_spec.rb +0 -1
  31. data/spec/refile/rails/attachment_helper_spec.rb +61 -0
  32. data/spec/refile/spec_helper.rb +26 -20
  33. data/spec/refile/test_app/app/controllers/normal_posts_controller.rb +10 -1
  34. data/spec/refile/test_app/app/controllers/presigned_posts_controller.rb +1 -0
  35. data/spec/refile/test_app/app/models/post.rb +1 -1
  36. data/spec/refile/test_app/app/views/normal_posts/index.html +5 -0
  37. data/spec/refile/test_app/app/views/normal_posts/show.html.erb +2 -0
  38. data/spec/refile/test_app/config/routes.rb +1 -1
  39. data/spec/refile/test_app.rb +13 -6
  40. data/spec/refile_spec.rb +39 -0
  41. metadata +51 -2
@@ -1,89 +1,5 @@
1
1
  module Refile
2
2
  module Attachment
3
- # @api private
4
- class Attachment
5
- attr_reader :record, :name, :cache, :store, :cache_id, :options, :errors
6
- attr_accessor :remove
7
-
8
- def initialize(record, name, **options)
9
- @record = record
10
- @name = name
11
- @options = options
12
- @cache = Refile.backends.fetch(@options[:cache].to_s)
13
- @store = Refile.backends.fetch(@options[:store].to_s)
14
- @errors = []
15
- end
16
-
17
- def id
18
- record.send(:"#{name}_id")
19
- end
20
-
21
- def id=(id)
22
- record.send(:"#{name}_id=", id)
23
- end
24
-
25
- def file
26
- if cached?
27
- cache.get(cache_id)
28
- elsif id and not id == ""
29
- store.get(id)
30
- end
31
- end
32
-
33
- def file=(uploadable)
34
- @cache_file = cache.upload(uploadable)
35
- @cache_id = @cache_file.id
36
- @errors = []
37
- rescue Refile::Invalid
38
- @errors = [:too_large]
39
- raise if @options[:raise_errors]
40
- end
41
-
42
- def download(url)
43
- if url and not url == ""
44
- raw_response = RestClient::Request.new(method: :get, url: url, raw_response: true).execute
45
- self.file = raw_response.file
46
- end
47
- rescue RestClient::Exception
48
- @errors = [:download_failed]
49
- raise if @options[:raise_errors]
50
- end
51
-
52
- def cache_id=(id)
53
- @cache_id = id unless @cache_file
54
- end
55
-
56
- def store!
57
- if remove?
58
- delete!
59
- elsif cached?
60
- file = store.upload(cache.get(cache_id))
61
- delete!
62
- self.id = file.id
63
- end
64
- end
65
-
66
- def delete!
67
- if cached?
68
- cache.delete(cache_id)
69
- @cache_id = nil
70
- @cache_file = nil
71
- end
72
- store.delete(id) if id
73
- self.id = nil
74
- end
75
-
76
- def remove?
77
- remove and remove != "" and remove !~ /\A0|false$\z/
78
- end
79
-
80
- private
81
-
82
- def cached?
83
- cache_id and not cache_id == ""
84
- end
85
- end
86
-
87
3
  # Macro which generates accessors for the given column which make it
88
4
  # possible to upload and retrieve previously uploaded files through the
89
5
  # generated accessors.
@@ -92,50 +8,67 @@ module Refile
92
8
  # should immediately raise an error, or save the error and defer handling
93
9
  # it until later.
94
10
  #
95
- # @param [String] name Name of the column which accessor are generated for
96
- # @param [#to_s] cache Name of a backend in +Refile.backends+ to use as transient cache
97
- # @param [#to_s] store Name of a backend in +Refile.backends+ to use as permanent store
98
- # @param [true, false] raise_errors Whether to raise errors in case an invalid file is assigned
99
- def attachment(name, cache: :cache, store: :store, raise_errors: true)
100
- attachment = :"#{name}_attachment"
11
+ # @param [String] name Name of the column which accessor are generated for
12
+ # @param [#to_s] cache Name of a backend in +Refile.backends+ to use as transient cache
13
+ # @param [#to_s] store Name of a backend in +Refile.backends+ to use as permanent store
14
+ # @param [true, false] raise_errors Whether to raise errors in case an invalid file is assigned
15
+ # @param [:image, nil] type The type of file that can be uploaded, currently +:image+ is the
16
+ # only valid value and restricts uploads to JPEG, PNG and GIF images
17
+ # @param [String, Array<String>, nil] extension Limit the uploaded file to the given extension or list of extensions
18
+ # @param [String, Array<String>, nil] content_type Limit the uploaded file to the given content type or list of content types
19
+ # @ignore
20
+ # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
21
+ def attachment(name, cache: :cache, store: :store, raise_errors: true, type: nil, extension: nil, content_type: nil)
22
+ mod = Module.new do
23
+ attacher = :"#{name}_attacher"
24
+
25
+ define_method attacher do
26
+ ivar = :"@#{attacher}"
27
+ instance_variable_get(ivar) or begin
28
+ instance_variable_set(ivar, Attacher.new(self, name,
29
+ cache: cache,
30
+ store: store,
31
+ raise_errors: raise_errors,
32
+ type: type,
33
+ extension: extension,
34
+ content_type: content_type
35
+ ))
36
+ end
37
+ end
101
38
 
102
- define_method attachment do
103
- ivar = :"@#{attachment}"
104
- instance_variable_get(ivar) or begin
105
- instance_variable_set(ivar, Attachment.new(self, name, cache: cache, store: store, raise_errors: raise_errors))
39
+ define_method "#{name}=" do |uploadable|
40
+ send(attacher).cache!(uploadable)
106
41
  end
107
- end
108
42
 
109
- define_method "#{name}=" do |uploadable|
110
- send(attachment).file = uploadable
111
- end
43
+ define_method name do
44
+ send(attacher).get
45
+ end
112
46
 
113
- define_method name do
114
- send(attachment).file
115
- end
47
+ define_method "#{name}_cache_id=" do |cache_id|
48
+ send(attacher).cache_id = cache_id
49
+ end
116
50
 
117
- define_method "#{name}_cache_id=" do |cache_id|
118
- send(attachment).cache_id = cache_id
119
- end
51
+ define_method "#{name}_cache_id" do
52
+ send(attacher).cache_id
53
+ end
120
54
 
121
- define_method "#{name}_cache_id" do
122
- send(attachment).cache_id
123
- end
55
+ define_method "remove_#{name}=" do |remove|
56
+ send(attacher).remove = remove
57
+ end
124
58
 
125
- define_method "remove_#{name}=" do |remove|
126
- send(attachment).remove = remove
127
- end
59
+ define_method "remove_#{name}" do
60
+ send(attacher).remove
61
+ end
128
62
 
129
- define_method "remove_#{name}" do
130
- send(attachment).remove
131
- end
63
+ define_method "remote_#{name}_url=" do |url|
64
+ send(attacher).download(url)
65
+ end
132
66
 
133
- define_method "remote_#{name}_url=" do |url|
134
- send(attachment).download(url)
67
+ define_method "remote_#{name}_url" do
68
+ end
135
69
  end
136
70
 
137
- define_method "remote_#{name}_url" do
138
- end
71
+ include mod
139
72
  end
140
73
  end
141
74
  end
@@ -1,7 +1,7 @@
1
1
  module Refile
2
2
  module Backend
3
3
  class FileSystem
4
- attr_reader :directory
4
+ attr_reader :directory, :max_size
5
5
 
6
6
  def initialize(directory, max_size: nil, hasher: Refile::RandomHasher.new)
7
7
  @hasher = hasher
@@ -53,11 +53,11 @@ module Refile
53
53
  end
54
54
 
55
55
  def exists?(id)
56
- ::File.exists?(path(id))
56
+ ::File.exist?(path(id))
57
57
  end
58
58
 
59
59
  def clear!(confirm = nil)
60
- raise ArgumentError, "are you sure? this will remove all files in the backend, call as `clear!(:confirm)` if you're sure you want to do this" unless confirm == :confirm
60
+ raise Refile::Confirm unless confirm == :confirm
61
61
  FileUtils.rm_rf(@directory)
62
62
  FileUtils.mkdir_p(@directory)
63
63
  end
@@ -2,10 +2,8 @@ require "aws-sdk"
2
2
 
3
3
  module Refile
4
4
  module Backend
5
-
6
5
  # A refile backend which stores files in Amazon S3
7
6
  class S3
8
-
9
7
  # Emulates an IO-object like interface on top of S3Object#read. To avoid
10
8
  # memory allocations and unnecessary complexity, this treats the `length`
11
9
  # parameter to read as a boolean flag instead. If given, it will read the
@@ -21,9 +19,7 @@ module Refile
21
19
  result = if length
22
20
  raise "closed" if @closed
23
21
 
24
- unless eof? # sets @peek
25
- @peek
26
- end
22
+ @peek unless eof? # sets @peek
27
23
  else
28
24
  @object.read
29
25
  end
@@ -55,14 +51,13 @@ module Refile
55
51
  end
56
52
  end
57
53
 
58
- Signature = Struct.new(:as, :id, :url, :fields)
59
-
60
- attr_reader :access_key_id
54
+ attr_reader :access_key_id, :max_size
61
55
 
62
- def initialize(access_key_id:, secret_access_key:, bucket:, max_size: nil, prefix: nil, hasher: Refile::RandomHasher.new)
56
+ def initialize(access_key_id:, secret_access_key:, bucket:, max_size: nil, prefix: nil, hasher: Refile::RandomHasher.new, **s3_options)
63
57
  @access_key_id = access_key_id
64
58
  @secret_access_key = secret_access_key
65
- @s3 = AWS::S3.new(access_key_id: access_key_id, secret_access_key: secret_access_key)
59
+ @s3_options = { access_key_id: access_key_id, secret_access_key: secret_access_key }.merge s3_options
60
+ @s3 = AWS::S3.new @s3_options
66
61
  @bucket_name = bucket
67
62
  @bucket = @s3.buckets[@bucket_name]
68
63
  @hasher = hasher
@@ -113,7 +108,7 @@ module Refile
113
108
  end
114
109
 
115
110
  def clear!(confirm = nil)
116
- raise ArgumentError, "are you sure? this will remove all files in the backend, call as `clear!(:confirm)` if you're sure you want to do this" unless confirm == :confirm
111
+ raise Refile::Confirm unless confirm == :confirm
117
112
  @bucket.objects.with_prefix(@prefix).delete_all
118
113
  end
119
114
 
@@ -121,7 +116,7 @@ module Refile
121
116
  id = RandomHasher.new.hash
122
117
  signature = @bucket.presigned_post(key: [*@prefix, id].join("/"))
123
118
  signature.where(content_length: @max_size) if @max_size
124
- Signature.new("file", id, signature.url.to_s, signature.fields)
119
+ Signature.new(as: "file", id: id, url: signature.url.to_s, fields: signature.fields)
125
120
  end
126
121
 
127
122
  def object(id)
data/lib/refile/file.rb CHANGED
@@ -48,9 +48,7 @@ module Refile
48
48
 
49
49
  def each
50
50
  if block_given?
51
- until eof?
52
- yield(read(Refile.read_chunk_size))
53
- end
51
+ yield(read(Refile.read_chunk_size)) until eof?
54
52
  else
55
53
  to_enum
56
54
  end
@@ -19,14 +19,17 @@ module Refile
19
19
  img.resize "#{width}x#{height}"
20
20
  end
21
21
 
22
- def fill(img, width, height, gravity = 'Center')
22
+ # @ignore
23
+ # rubocop:disable Metrics/AbcSize
24
+ # FIXME: test and rewrite to simpler implementation!
25
+ def fill(img, width, height, gravity = "Center")
23
26
  width = width.to_i
24
27
  height = height.to_i
25
28
  cols, rows = img[:dimensions]
26
29
  img.combine_options do |cmd|
27
30
  if width != cols || height != rows
28
- scale_x = width/cols.to_f
29
- scale_y = height/rows.to_f
31
+ scale_x = width / cols.to_f
32
+ scale_y = height / rows.to_f
30
33
  if scale_x >= scale_y
31
34
  cols = (scale_x * (cols + 0.5)).round
32
35
  rows = (scale_x * (rows + 0.5)).round
@@ -1,58 +1,55 @@
1
1
  module Refile
2
2
  module AttachmentHelper
3
- def attachment_url(record, name, *args, filename: nil, format: nil)
3
+ def attachment_url(record, name, *args, filename: nil, format: nil, host: nil)
4
4
  file = record.send(name)
5
5
  return unless file
6
6
 
7
7
  filename ||= name.to_s
8
8
 
9
9
  backend_name = Refile.backends.key(file.backend)
10
- host = Refile.host || request.base_url
10
+ host = host || Refile.host || request.base_url
11
11
 
12
12
  filename = filename.parameterize("_")
13
13
  filename << "." << format.to_s if format
14
14
 
15
- ::File.join(host, main_app.refile_app_path, backend_name, *args.map(&:to_s), file.id, filename)
15
+ ::File.join(host, main_app.refile_app_path, backend_name, *args.map(&:to_s), file.id.to_s, filename)
16
16
  end
17
17
 
18
- def attachment_image_tag(record, name, *args, fallback: nil, format: nil, **options)
18
+ def attachment_image_tag(record, name, *args, fallback: nil, format: nil, host: nil, **options)
19
19
  file = record.send(name)
20
20
  classes = ["attachment", record.class.model_name.singular, name, *options[:class]]
21
21
 
22
22
  if file
23
- image_tag(attachment_url(record, name, *args, format: format), options.merge(class: classes))
23
+ image_tag(attachment_url(record, name, *args, format: format, host: host), options.merge(class: classes))
24
24
  elsif fallback
25
25
  classes << "fallback"
26
26
  image_tag(fallback, options.merge(class: classes))
27
27
  end
28
28
  end
29
29
 
30
+ # @ignore
31
+ # rubocop:disable Metrics/AbcSize
30
32
  def attachment_field(object_name, method, options = {})
33
+ options[:data] ||= {}
34
+
31
35
  if options[:object]
32
- cache = options[:object].send(:"#{method}_attachment").cache
36
+ attacher = options[:object].send(:"#{method}_attacher")
37
+ options[:accept] = attacher.accept
33
38
 
34
39
  if options[:direct]
35
- host = Refile.host || request.base_url
36
- backend_name = Refile.backends.key(cache)
40
+ host = options[:host] || Refile.host || request.base_url
41
+ backend_name = Refile.backends.key(attacher.cache)
37
42
 
38
- options[:data] ||= {}
39
- options[:data][:direct] = true
40
- options[:data][:as] = "file"
41
- options[:data][:url] = ::File.join(host, main_app.refile_app_path, backend_name)
43
+ url = ::File.join(host, main_app.refile_app_path, backend_name)
44
+ options[:data].merge!(direct: true, as: "file", url: url)
42
45
  end
43
46
 
44
- if options[:presigned] and cache.respond_to?(:presign)
45
- signature = cache.presign
46
- options[:data] ||= {}
47
- options[:data][:direct] = true
48
- options[:data][:id] = signature.id
49
- options[:data][:url] = signature.url
50
- options[:data][:fields] = signature.fields
51
- options[:data][:as] = signature.as
47
+ if options[:presigned] and attacher.cache.respond_to?(:presign)
48
+ options[:data].merge!(direct: true).merge!(attacher.cache.presign.as_json)
52
49
  end
53
50
  end
54
51
  hidden_field(object_name, :"#{method}_cache_id", options.slice(:object)) +
55
- file_field(object_name, method, options)
52
+ file_field(object_name, method, options)
56
53
  end
57
54
  end
58
55
  end
data/lib/refile/rails.rb CHANGED
@@ -23,7 +23,8 @@ module Refile
23
23
  end
24
24
 
25
25
  initializer "refile.app" do
26
- Refile.app = Refile::App.new(logger: Rails.logger)
26
+ Refile.logger = Rails.logger
27
+ Refile.app = Refile::App.new
27
28
  end
28
29
  end
29
30
  end
@@ -1,10 +1,11 @@
1
- # A file hasher which ignores the file contents and always returns a random string.
2
- class Refile::RandomHasher
3
-
4
- # Generate a random string
5
- #
6
- # @return [String]
7
- def hash(uploadable=nil)
8
- SecureRandom.hex(30)
1
+ module Refile
2
+ # A file hasher which ignores the file contents and always returns a random string.
3
+ class RandomHasher
4
+ # Generate a random string
5
+ #
6
+ # @return [String]
7
+ def hash(_uploadable = nil)
8
+ SecureRandom.hex(30)
9
+ end
9
10
  end
10
11
  end
@@ -0,0 +1,16 @@
1
+ module Refile
2
+ class Signature
3
+ attr_reader :as, :id, :url, :fields
4
+
5
+ def initialize(as:, id:, url:, fields:)
6
+ @as = as
7
+ @id = id
8
+ @url = url
9
+ @fields = fields
10
+ end
11
+
12
+ def as_json(*)
13
+ { as: @as, id: @id, url: @url, fields: @fields }
14
+ end
15
+ end
16
+ end
@@ -1,3 +1,3 @@
1
1
  module Refile
2
- VERSION = "0.3.0"
2
+ VERSION = "0.4.0"
3
3
  end
data/lib/refile.rb CHANGED
@@ -2,12 +2,20 @@ require "uri"
2
2
  require "fileutils"
3
3
  require "tempfile"
4
4
  require "rest_client"
5
+ require "logger"
6
+ require "mime/types"
5
7
 
6
8
  module Refile
7
9
  class Invalid < StandardError; end
10
+ class Confirm < StandardError
11
+ def message
12
+ "are you sure? this will remove all files in the backend, call as \
13
+ `clear!(:confirm)` if you're sure you want to do this"
14
+ end
15
+ end
16
+ ONE_YEAR_IN_SECONDS = 31_557_600
8
17
 
9
18
  class << self
10
-
11
19
  # The number of bytes to read when files are streamed. Refile
12
20
  # uses this in a couple of places where files should be streamed
13
21
  # in a memory efficient way instead of reading the entire file into
@@ -40,6 +48,29 @@ module Refile
40
48
  # @return [Array[String]]
41
49
  attr_accessor :direct_upload
42
50
 
51
+ # Logger that should be used by rack application
52
+ #
53
+ # @return [Logger]
54
+ attr_accessor :logger
55
+
56
+ # Value for Access-Control-Allow-Origin header
57
+ #
58
+ # @return [String]
59
+ attr_accessor :allow_origin
60
+
61
+ # Value for Cache-Control: max-age=<value in seconds> header
62
+ attr_accessor :content_max_age
63
+
64
+ # Where should the rack application be mounted?
65
+ # The default is 'attachments'
66
+ attr_accessor :mount_point
67
+
68
+ # Should the rack application be automounted in a Rails app?
69
+ # The default is true.
70
+ # If set to false then Refile.app should be mounted in the Rails application
71
+ # routes.rb with the options `at: Refile.mount_point, as: :refile_app`
72
+ attr_accessor :automount
73
+
43
74
  # A global registry of backends.
44
75
  #
45
76
  # @return [Hash{String => Backend}]
@@ -146,9 +177,31 @@ module Refile
146
177
  end
147
178
  true
148
179
  end
180
+
181
+ def extract_filename(uploadable)
182
+ path = if uploadable.respond_to?(:original_filename)
183
+ uploadable.original_filename
184
+ elsif uploadable.respond_to?(:path)
185
+ uploadable.path
186
+ end
187
+ ::File.basename(path) if path
188
+ end
189
+
190
+ def extract_content_type(uploadable)
191
+ if uploadable.respond_to?(:content_type)
192
+ uploadable.content_type
193
+ else
194
+ filename = extract_filename(uploadable)
195
+ if filename
196
+ content_type = MIME::Types.of(filename).first
197
+ content_type.to_s if content_type
198
+ end
199
+ end
200
+ end
149
201
  end
150
202
 
151
203
  require "refile/version"
204
+ require "refile/attacher"
152
205
  require "refile/attachment"
153
206
  require "refile/random_hasher"
154
207
  require "refile/file"
@@ -162,4 +215,9 @@ Refile.configure do |config|
162
215
  # one?
163
216
  config.read_chunk_size = 3000
164
217
  config.direct_upload = ["cache"]
218
+ config.allow_origin = "*"
219
+ config.logger = Logger.new(STDOUT)
220
+ config.mount_point = "attachments"
221
+ config.automount = true
222
+ config.content_max_age = Refile::ONE_YEAR_IN_SECONDS
165
223
  end
data/refile.gemspec CHANGED
@@ -1,24 +1,27 @@
1
1
  # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
2
+ lib = File.expand_path("../lib", __FILE__)
3
3
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'refile/version'
4
+ require "refile/version"
5
5
 
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = "refile"
8
8
  spec.version = Refile::VERSION
9
9
  spec.authors = ["Jonas Nicklas"]
10
10
  spec.email = ["jonas.nicklas@gmail.com"]
11
- spec.summary = %q{Simple and powerful file upload library}
11
+ spec.summary = "Simple and powerful file upload library"
12
12
  spec.homepage = ""
13
13
  spec.license = "MIT"
14
14
 
15
15
  spec.files = `git ls-files -z`.split("\x0")
16
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
- spec.require_paths = ["lib", "spec"]
16
+ spec.executables = spec.files.grep(/^bin\//) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(/^(test|spec|features)\//)
18
+ spec.require_paths = %w[lib spec]
19
19
 
20
20
  spec.required_ruby_version = ">= 2.1.0"
21
+
21
22
  spec.add_dependency "rest-client", "~> 1.7.2"
23
+ spec.add_dependency "sinatra", "~> 1.4.5"
24
+ spec.add_dependency "mime-types"
22
25
 
23
26
  spec.add_development_dependency "webmock", "~> 1.20.4"
24
27
  spec.add_development_dependency "bundler", "~> 1.6"
@@ -34,4 +37,5 @@ Gem::Specification.new do |spec|
34
37
  spec.add_development_dependency "sqlite3"
35
38
  spec.add_development_dependency "selenium-webdriver"
36
39
  spec.add_development_dependency "yard"
40
+ spec.add_development_dependency "rubocop"
37
41
  end
@@ -18,11 +18,12 @@ describe Refile::App do
18
18
  end
19
19
 
20
20
  it "returns a 404 if the file doesn't exist" do
21
- file = Refile.store.upload(StringIO.new("hello"))
21
+ Refile.store.upload(StringIO.new("hello"))
22
22
 
23
23
  get "/store/doesnotexist/hello"
24
24
 
25
25
  expect(last_response.status).to eq(404)
26
+ expect(last_response.content_type).to eq("text/plain;charset=utf-8")
26
27
  expect(last_response.body).to eq("not found")
27
28
  end
28
29
 
@@ -32,12 +33,13 @@ describe Refile::App do
32
33
  get "/doesnotexist/#{file.id}/hello"
33
34
 
34
35
  expect(last_response.status).to eq(404)
36
+ expect(last_response.content_type).to eq("text/plain;charset=utf-8")
35
37
  expect(last_response.body).to eq("not found")
36
38
  end
37
39
 
38
40
  context "with allow origin" do
39
- def app
40
- Refile::App.new(allow_origin: "example.com")
41
+ before(:each) do
42
+ allow(Refile).to receive(:allow_origin).and_return("example.com")
41
43
  end
42
44
 
43
45
  it "sets CORS header" do
@@ -51,12 +53,31 @@ describe Refile::App do
51
53
  end
52
54
  end
53
55
 
56
+ it "returns a 200 for head requests" do
57
+ file = Refile.store.upload(StringIO.new("hello"))
58
+
59
+ head "/store/#{file.id}/hello"
60
+
61
+ expect(last_response.status).to eq(200)
62
+ expect(last_response.body).to be_empty
63
+ end
64
+
65
+ it "returns a 404 for head requests if the file doesn't exist" do
66
+ Refile.store.upload(StringIO.new("hello"))
67
+
68
+ head "/store/doesnotexist/hello"
69
+
70
+ expect(last_response.status).to eq(404)
71
+ expect(last_response.body).to be_empty
72
+ end
73
+
54
74
  it "returns a 404 for non get requests" do
55
75
  file = Refile.store.upload(StringIO.new("hello"))
56
76
 
57
77
  post "/store/#{file.id}/hello"
58
78
 
59
79
  expect(last_response.status).to eq(404)
80
+ expect(last_response.content_type).to eq("text/plain;charset=utf-8")
60
81
  expect(last_response.body).to eq("not found")
61
82
  end
62
83
  end
@@ -68,6 +89,7 @@ describe Refile::App do
68
89
  get "/store/doesnotexist/#{file.id}/hello"
69
90
 
70
91
  expect(last_response.status).to eq(404)
92
+ expect(last_response.content_type).to eq("text/plain;charset=utf-8")
71
93
  expect(last_response.body).to eq("not found")
72
94
  end
73
95
 
@@ -114,6 +136,7 @@ describe Refile::App do
114
136
  post "/store", file: file
115
137
 
116
138
  expect(last_response.status).to eq(404)
139
+ expect(last_response.content_type).to eq("text/plain;charset=utf-8")
117
140
  expect(last_response.body).to eq("not found")
118
141
  end
119
142
 
@@ -130,6 +153,7 @@ describe Refile::App do
130
153
  get "/store"
131
154
 
132
155
  expect(last_response.status).to eq(404)
156
+ expect(last_response.content_type).to eq("text/plain;charset=utf-8")
133
157
  expect(last_response.body).to eq("not found")
134
158
  end
135
159
 
@@ -137,7 +161,7 @@ describe Refile::App do
137
161
  get "/"
138
162
 
139
163
  expect(last_response.status).to eq(404)
164
+ expect(last_response.content_type).to eq("text/plain;charset=utf-8")
140
165
  expect(last_response.body).to eq("not found")
141
166
  end
142
167
  end
143
-