refile 0.2.4 → 0.2.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 32c960945a6e5eb20121a3eb7d4838574dd3b946
4
- data.tar.gz: 4ae9bd94ff29d3db2f74655d6db4f9e4379f25fd
3
+ metadata.gz: 66457e73d63efd2eafa3b150152d7615fc4c940a
4
+ data.tar.gz: e7e6744055b448670fd328a29fa97d122a332b4a
5
5
  SHA512:
6
- metadata.gz: ba04479216634a21b37f455e2d88184e9f3e0095110aa49b3f2ec7c72f6aa0b95c96077e94e4eda7aaf5c31d2fc9630f3a2dc5ad134d1ae099dceb044404f31b
7
- data.tar.gz: 1c58cc57c1eb4a7b5d42b229bb375071709d105ac5b405f999e6bcfb97500cb538e412f845d84f00df253177c7fb7de9f2c9d355bb0bc8bf4442185ea45d4e1e
6
+ metadata.gz: bf353ab538163c2716a2114f887855e87b083493186911dc61ca064a971d96b1b72a7282ec3531530da66187964f15243243d8b027655c5a192336e61e0a50c6
7
+ data.tar.gz: ebd0c248bdccaa5ad714764df5fc2dcac65922a0148019a9a897fc676bf1cabde9cb74794a7a5620c23d238e2f56f697d8b3ee4ebc163395deaf01ba3ab80fea
data/.rspec CHANGED
@@ -1 +1,2 @@
1
1
  -r refile/spec_helper
2
+ -r pry
data/History.md CHANGED
@@ -1,3 +1,12 @@
1
+ # 0.2.5
2
+
3
+ Release date: 2014-12-12
4
+
5
+ - [ADDED] CarrierWave style `remove_` attribute
6
+ - [ADDED] Files are deleted after model is destroyed
7
+ - [FIXED] Spec files can be required by external gems
8
+ - [FIXED] Refile should work inside other Rails engines
9
+
1
10
  # 0.2.4
2
11
 
3
12
  Release date: 2014-12-08
data/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  # Refile
2
-
2
+ [![Gem Version](https://badge.fury.io/rb/refile.svg)](http://badge.fury.io/rb/refile)
3
3
  [![Build Status](https://travis-ci.org/elabs/refile.svg?branch=master)](https://travis-ci.org/elabs/refile)
4
4
 
5
5
  Refile is a modern file upload library for Ruby applications. It is simple, yet
@@ -139,6 +139,16 @@ For example:
139
139
  Refile.cache = Refile::Backend::S3.new(max_size: 10.megabytes, ...)
140
140
  ```
141
141
 
142
+ The Refile gem ships with [S3](lib/refile/backends/s3.rb) and
143
+ [FileSystem](lib/refile/backends/file_system.rb) backends. Additional backends
144
+ are provided by other gems.
145
+
146
+ - [Fog](https://github.com/elabs/refile-fog) provides support for a ton of
147
+ different cloud storage providers, including Google Storage and Rackspace
148
+ CloudFiles.
149
+ - [Postgresql](https://github.com/krists/refile-postgres)
150
+ - [In Memory](https://github.com/jnicklas/refile-memory)
151
+
142
152
  ### Uploadable
143
153
 
144
154
  The `upload` method on backends can be called with a variety of objects. It
@@ -212,7 +222,7 @@ Refile includes a Rack application (an endpoint, not a middleware). This applica
212
222
  streams files from backends and can even accept file uploads and upload them to
213
223
  backends.
214
224
 
215
- **Important:** Unlike other file upload solutions, Refile always streams your files thorugh your
225
+ **Important:** Unlike other file upload solutions, Refile always streams your files through your
216
226
  application. It cannot generate URLs to your files. This means that you should
217
227
  **always** put a CDN or other HTTP cache in front of your application. Serving
218
228
  files through your app takes a lot of resources and you want it to happen rarely.
@@ -226,7 +236,7 @@ up, simply configure Refile to use your CDN:
226
236
  Refile.host = "//your-dist-url.cloudfront.net"
227
237
  ```
228
238
 
229
- Using a [procol-relative URL](http://www.paulirish.com/2010/the-protocol-relative-url/) for `Refile.host` is recommended.
239
+ Using a [protocol-relative URL](http://www.paulirish.com/2010/the-protocol-relative-url/) for `Refile.host` is recommended.
230
240
 
231
241
  ### Mounting
232
242
 
@@ -312,13 +322,26 @@ The `attachment_url` helper can then be used for generating URLs for the uploade
312
322
  files:
313
323
 
314
324
  ``` erb
315
- <%= image_tag attachment_url(@user, :profile_image) %>
325
+ <%= link_to "Image", attachment_url(@user, :profile_image) %>
316
326
  ```
317
327
 
318
328
  Any additional arguments to it are included in the URL as processor arguments:
319
329
 
320
330
  ``` erb
321
- <%= image_tag attachment_url(@user, :profile_image, :fill, 300, 300) %>
331
+ <%= link_to "Image", attachment_url(@user, :profile_image, :fill, 300, 300) %>
332
+ ```
333
+
334
+ There's also a helper for generating image tags:
335
+
336
+ ``` erb
337
+ <%= attachment_image_tag(@user, :profile_image, :fill, 300, 300) %>
338
+ ```
339
+
340
+ With this helper you can specify an image which is used as a fallback in case
341
+ no file has been uploaded:
342
+
343
+ ``` erb
344
+ <%= attachment_image_tag(@user, :profile_image, :fill, 300, 300, fallback: "defaul.png") %>
322
345
  ```
323
346
 
324
347
  ## 5. JavaScript library
@@ -454,6 +477,35 @@ Refile's JavaScript library requires HTML5 features which are unavailable on
454
477
  IE9 and earlier versions. All other major browsers are supported. Note though
455
478
  that it has not yet been extensively tested.
456
479
 
480
+ ## Removing attached files
481
+
482
+ File input fields unfortunately do not have the option of removing an already
483
+ uploaded file. This is problematic when editing a model which has a file attached
484
+ and the user wants to remove this file. To work around this, Refile automatically
485
+ adds an attribute to your model when you use the `attachment` method, which is
486
+ designed to be used with a checkbox in a form.
487
+
488
+ ``` erb
489
+ <%= form_for @user do |form| %>
490
+ <%= form.label :profile_image %>
491
+ <%= form.attachment_field :profile_image %>
492
+
493
+ <%= form.check_box :remove_profile_image %>
494
+ <%= form.label :remove_profile_image %>
495
+ <% end %>
496
+ ```
497
+
498
+ Don't forget to permit this attribute in your controller:
499
+
500
+ ``` ruby
501
+ def user_params
502
+ params.require(:user).permit(:profile_image, :profile_image_cache_id, :remove_profile_image)
503
+ end
504
+ ```
505
+
506
+ Now when you check this checkbox and submit the form, the previously attached
507
+ file will be removed.
508
+
457
509
  ## Cache expiry
458
510
 
459
511
  Files will accumulate in your cache, and you'll probably want to remove them
@@ -4,6 +4,7 @@ module Refile
4
4
 
5
5
  class Attachment
6
6
  attr_reader :record, :name, :cache, :store, :cache_id, :options
7
+ attr_accessor :remove
7
8
 
8
9
  def initialize(record, name, **options)
9
10
  @record = record
@@ -44,19 +45,38 @@ module Refile
44
45
  end
45
46
 
46
47
  def store!
47
- if cache_id and not cache_id == ""
48
+ if remove?
49
+ delete!
50
+ elsif cached?
48
51
  file = store.upload(cache.get(cache_id))
49
- cache.delete(cache_id)
50
- store.delete(id) if id
52
+ delete!
51
53
  self.id = file.id
54
+ end
55
+ end
56
+
57
+ def delete!
58
+ if cached?
59
+ cache.delete(cache_id)
52
60
  @cache_id = nil
53
61
  @cache_file = nil
54
62
  end
63
+ store.delete(id) if id
64
+ self.id = nil
65
+ end
66
+
67
+ def remove?
68
+ remove.present? and remove !~ /\A0|false$\z/
55
69
  end
56
70
 
57
71
  def errors
58
72
  @errors
59
73
  end
74
+
75
+ private
76
+
77
+ def cached?
78
+ cache_id and not cache_id == ""
79
+ end
60
80
  end
61
81
 
62
82
  def attachment(name, cache: :cache, store: :store, raise_errors: true)
@@ -84,6 +104,14 @@ module Refile
84
104
  define_method "#{name}_cache_id" do
85
105
  send(attachment).cache_id
86
106
  end
107
+
108
+ define_method "remove_#{name}=" do |remove|
109
+ send(attachment).remove = remove
110
+ end
111
+
112
+ define_method "remove_#{name}" do
113
+ send(attachment).remove
114
+ end
87
115
  end
88
116
  end
89
117
  end
@@ -16,6 +16,10 @@ module Refile
16
16
  before_save do
17
17
  send(attachment).store!
18
18
  end
19
+
20
+ after_destroy do
21
+ send(attachment).delete!
22
+ end
19
23
  end
20
24
  end
21
25
  end
@@ -1,4 +1,5 @@
1
1
  require "refile"
2
+ require "refile/rails/attachment_helper"
2
3
 
3
4
  module Refile
4
5
  module Controller
@@ -28,6 +29,7 @@ module Refile
28
29
  require "refile/attachment/active_record"
29
30
  end
30
31
 
32
+ ActionView::Base.send(:include, Refile::AttachmentHelper)
31
33
  ActionView::Helpers::FormBuilder.send(:include, AttachmentFieldHelper)
32
34
  end
33
35
 
@@ -0,0 +1,57 @@
1
+ module Refile
2
+ module AttachmentHelper
3
+ def attachment_url(record, name, *args, filename: nil, format: nil)
4
+ file = record.send(name)
5
+
6
+ filename ||= name.to_s
7
+
8
+ backend_name = Refile.backends.key(file.backend)
9
+ host = Refile.host || request.base_url
10
+
11
+ filename = filename.parameterize("_")
12
+ filename << "." << format.to_s if format
13
+
14
+ ::File.join(host, main_app.refile_app_path, backend_name, *args.map(&:to_s), file.id, filename)
15
+ end
16
+
17
+ def attachment_image_tag(record, name, *args, fallback: nil, format: nil, **options)
18
+ file = record.send(name)
19
+ classes = ["attachment", record.class.model_name.singular, name, *options[:class]]
20
+
21
+ if file
22
+ image_tag(attachment_url(record, name, *args, format: format), options.merge(class: classes))
23
+ elsif fallback
24
+ classes << "fallback"
25
+ image_tag(fallback, options.merge(class: classes))
26
+ end
27
+ end
28
+
29
+ def attachment_field(object_name, method, options = {})
30
+ if options[:object]
31
+ cache = options[:object].send(:"#{method}_attachment").cache
32
+
33
+ if options[:direct]
34
+ host = Refile.host || request.base_url
35
+ backend_name = Refile.backends.key(cache)
36
+
37
+ options[:data] ||= {}
38
+ options[:data][:direct] = true
39
+ options[:data][:as] = "file"
40
+ options[:data][:url] = ::File.join(host, main_app.refile_app_path, backend_name)
41
+ end
42
+
43
+ if options[:presigned] and cache.respond_to?(:presign)
44
+ signature = cache.presign
45
+ options[:data] ||= {}
46
+ options[:data][:direct] = true
47
+ options[:data][:id] = signature.id
48
+ options[:data][:url] = signature.url
49
+ options[:data][:fields] = signature.fields
50
+ options[:data][:as] = signature.as
51
+ end
52
+ end
53
+ hidden_field(object_name, :"#{method}_cache_id", options.slice(:object)) +
54
+ file_field(object_name, method, options)
55
+ end
56
+ end
57
+ end
@@ -1,3 +1,3 @@
1
1
  module Refile
2
- VERSION = "0.2.4"
2
+ VERSION = "0.2.5"
3
3
  end
@@ -15,7 +15,7 @@ Gem::Specification.new do |spec|
15
15
  spec.files = `git ls-files -z`.split("\x0")
16
16
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
17
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
- spec.require_paths = ["lib"]
18
+ spec.require_paths = ["lib", "spec"]
19
19
 
20
20
  spec.required_ruby_version = ">= 2.1.0"
21
21
 
@@ -80,6 +80,67 @@ describe Refile::Attachment do
80
80
  expect(Refile.cache.get(cache.id).exists?).to be_falsy
81
81
  expect(Refile.store.get(file.id).exists?).to be_falsy
82
82
  end
83
+
84
+ it "removes an uploaded file when remove? returns true" do
85
+ file = Refile.store.upload(Refile::FileDouble.new("hello"))
86
+ instance.document_id = file.id
87
+
88
+ instance.document_attachment.remove = true
89
+ instance.document_attachment.store!
90
+
91
+ expect(instance.document_id).to be_nil
92
+ expect(Refile.store.exists?(file.id)).to be_falsy
93
+ end
94
+ end
95
+
96
+ describe ":name_attachment.delete!" do
97
+ it "deletes a stored file" do
98
+ file = Refile.store.upload(Refile::FileDouble.new("hello"))
99
+ instance.document_id = file.id
100
+
101
+ instance.document_attachment.delete!
102
+
103
+ expect(instance.document_id).to be_nil
104
+ expect(Refile.store.exists?(file.id)).to be_falsy
105
+ end
106
+
107
+ it "deletes a cached file" do
108
+ file = Refile.cache.upload(Refile::FileDouble.new("hello"))
109
+ instance.document_cache_id = file.id
110
+
111
+ instance.document_attachment.delete!
112
+
113
+ expect(instance.document_id).to be_nil
114
+ expect(instance.document_cache_id).to be_nil
115
+ expect(Refile.cache.exists?(file.id)).to be_falsy
116
+ end
117
+ end
118
+
119
+ describe ":name_attachment.remove?" do
120
+ it "should be true when the value is truthy" do
121
+ instance.document_attachment.remove = true
122
+ expect(instance.document_attachment.remove?).to be_truthy
123
+ end
124
+
125
+ it "should be false when the value is falsey" do
126
+ instance.document_attachment.remove = false
127
+ expect(instance.document_attachment.remove?).to be_falsy
128
+ end
129
+
130
+ it "should be false when the value is ''" do
131
+ instance.document_attachment.remove = ''
132
+ expect(instance.document_attachment.remove?).to be_falsy
133
+ end
134
+
135
+ it "should be false when the value is '0'" do
136
+ instance.document_attachment.remove = '0'
137
+ expect(instance.document_attachment.remove?).to be_falsy
138
+ end
139
+
140
+ it "should be false when the value is 'false'" do
141
+ instance.document_attachment.remove = 'false'
142
+ expect(instance.document_attachment.remove?).to be_falsy
143
+ end
83
144
  end
84
145
 
85
146
  describe ":name_attachment.error" do
@@ -44,4 +44,20 @@ feature "Normal HTTP Post file uploads" do
44
44
  click_link("Convert to Upper")
45
45
  expect(page.source.chomp).to eq("HELLO")
46
46
  end
47
+
48
+ scenario "Successfully remove an uploaded file" do
49
+ visit "/normal/posts/new"
50
+ fill_in "Title", with: "A cool post"
51
+ attach_file "Document", path("hello.txt")
52
+ click_button "Create"
53
+
54
+ expect(page).to have_selector("h1", text: "A cool post")
55
+ expect(page).to have_selector(:link, "Document")
56
+ click_link("Edit")
57
+
58
+ check "Remove document"
59
+ click_button "Update"
60
+ expect(page).to have_selector("h1", text: "A cool post")
61
+ expect(page).to_not have_selector(:link, "Document")
62
+ end
47
63
  end
@@ -1,4 +1,3 @@
1
- require "pry"
2
1
  require "refile"
3
2
  require "refile/backend_examples"
4
3
 
@@ -1,14 +1,18 @@
1
1
  class NormalPostsController < ApplicationController
2
+ def show
3
+ @post = Post.find(params[:id])
4
+ end
5
+
2
6
  def new
3
7
  @post = Post.new
4
8
  end
5
9
 
6
- def show
10
+ def edit
7
11
  @post = Post.find(params[:id])
8
12
  end
9
13
 
10
14
  def create
11
- @post = Post.new(params.require(:post).permit(:title, :image, :image_cache_id, :document, :document_cache_id))
15
+ @post = Post.new(post_params)
12
16
 
13
17
  if @post.save
14
18
  redirect_to [:normal, @post]
@@ -16,4 +20,20 @@ class NormalPostsController < ApplicationController
16
20
  render :new
17
21
  end
18
22
  end
23
+
24
+ def update
25
+ @post = Post.find(params[:id])
26
+
27
+ if @post.update_attributes(post_params)
28
+ redirect_to [:normal, @post]
29
+ else
30
+ render :edit
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ def post_params
37
+ params.require(:post).permit(:title, :image, :image_cache_id, :document, :document_cache_id, :remove_document)
38
+ end
19
39
  end
@@ -0,0 +1,24 @@
1
+ <%= form_for [:normal, @post] do |form| %>
2
+ <p>
3
+ <%= @post.errors.full_messages.to_sentence %>
4
+ </p>
5
+ <p>
6
+ <%= form.label :title %>
7
+ <%= form.text_field :title %>
8
+ </p>
9
+ <p>
10
+ <%= form.label :image %>
11
+ <%= form.attachment_field :image %>
12
+ </p>
13
+ <p>
14
+ <%= form.label :document %>
15
+ <%= form.attachment_field :document %>
16
+ </p>
17
+ <p>
18
+ <%= form.label :remove_document %>
19
+ <%= form.check_box :remove_document %>
20
+ </p>
21
+ <p>
22
+ <%= form.submit %>
23
+ </p>
24
+ <% end %>
@@ -0,0 +1 @@
1
+ <%= render "form" %>
@@ -1,20 +1 @@
1
- <%= form_for [:normal, @post] do |form| %>
2
- <p>
3
- <%= @post.errors.full_messages.to_sentence %>
4
- </p>
5
- <p>
6
- <%= form.label :title %>
7
- <%= form.text_field :title %>
8
- </p>
9
- <p>
10
- <%= form.label :image %>
11
- <%= form.attachment_field :image %>
12
- </p>
13
- <p>
14
- <%= form.label :document %>
15
- <%= form.attachment_field :document %>
16
- </p>
17
- <p>
18
- <%= form.submit "Create" %>
19
- </p>
20
- <% end %>
1
+ <%= render "form" %>
@@ -11,3 +11,5 @@
11
11
  <% if @post.document %>
12
12
  <%= link_to "Convert to Upper", attachment_url(@post, :document, :convert_case, format: "up") %>
13
13
  <% end %>
14
+
15
+ <%= link_to "Edit", edit_normal_post_url(@post) %>
@@ -2,7 +2,7 @@ Refile::TestApp.routes.draw do
2
2
  root to: "home#index"
3
3
 
4
4
  scope path: "normal", as: "normal" do
5
- resources :posts, only: [:new, :create, :show], controller: "normal_posts"
5
+ resources :posts, only: [:show, :new, :edit, :create, :update], controller: "normal_posts"
6
6
  end
7
7
 
8
8
  scope path: "direct", as: "direct" do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: refile
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.4
4
+ version: 0.2.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jonas Nicklas
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-12-08 00:00:00.000000000 Z
11
+ date: 2014-12-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -194,7 +194,6 @@ files:
194
194
  - README.md
195
195
  - Rakefile
196
196
  - app/assets/javascripts/refile.js
197
- - app/helpers/attachment_helper.rb
198
197
  - config.ru
199
198
  - config/locales/en.yml
200
199
  - config/routes.rb
@@ -207,6 +206,7 @@ files:
207
206
  - lib/refile/file.rb
208
207
  - lib/refile/image_processing.rb
209
208
  - lib/refile/rails.rb
209
+ - lib/refile/rails/attachment_helper.rb
210
210
  - lib/refile/random_hasher.rb
211
211
  - lib/refile/version.rb
212
212
  - refile.gemspec
@@ -232,6 +232,8 @@ files:
232
232
  - spec/refile/test_app/app/views/direct_posts/new.html.erb
233
233
  - spec/refile/test_app/app/views/home/index.html.erb
234
234
  - spec/refile/test_app/app/views/layouts/application.html.erb
235
+ - spec/refile/test_app/app/views/normal_posts/_form.html.erb
236
+ - spec/refile/test_app/app/views/normal_posts/edit.html.erb
235
237
  - spec/refile/test_app/app/views/normal_posts/new.html.erb
236
238
  - spec/refile/test_app/app/views/normal_posts/show.html.erb
237
239
  - spec/refile/test_app/app/views/presigned_posts/new.html.erb
@@ -247,6 +249,7 @@ post_install_message:
247
249
  rdoc_options: []
248
250
  require_paths:
249
251
  - lib
252
+ - spec
250
253
  required_ruby_version: !ruby/object:Gem::Requirement
251
254
  requirements:
252
255
  - - ">="
@@ -286,6 +289,8 @@ test_files:
286
289
  - spec/refile/test_app/app/views/direct_posts/new.html.erb
287
290
  - spec/refile/test_app/app/views/home/index.html.erb
288
291
  - spec/refile/test_app/app/views/layouts/application.html.erb
292
+ - spec/refile/test_app/app/views/normal_posts/_form.html.erb
293
+ - spec/refile/test_app/app/views/normal_posts/edit.html.erb
289
294
  - spec/refile/test_app/app/views/normal_posts/new.html.erb
290
295
  - spec/refile/test_app/app/views/normal_posts/show.html.erb
291
296
  - spec/refile/test_app/app/views/presigned_posts/new.html.erb
@@ -1,55 +0,0 @@
1
- module AttachmentHelper
2
- def attachment_url(record, name, *args, filename: nil, format: nil)
3
- file = record.send(name)
4
-
5
- filename ||= name.to_s
6
-
7
- backend_name = Refile.backends.key(file.backend)
8
- host = Refile.host || request.base_url
9
-
10
- filename = filename.parameterize("_")
11
- filename << "." << format.to_s if format
12
-
13
- File.join(host, refile_app_path, backend_name, *args.map(&:to_s), file.id, filename)
14
- end
15
-
16
- def attachment_image_tag(record, name, *args, fallback: nil, format: nil, **options)
17
- file = record.send(name)
18
- classes = ["attachment", record.class.model_name.singular, name, *options[:class]]
19
-
20
- if file
21
- image_tag(attachment_url(record, name, *args, format: format), options.merge(class: classes))
22
- elsif fallback
23
- classes << "fallback"
24
- image_tag(fallback, options.merge(class: classes))
25
- end
26
- end
27
-
28
- def attachment_field(object_name, method, options = {})
29
- if options[:object]
30
- cache = options[:object].send(:"#{method}_attachment").cache
31
-
32
- if options[:direct]
33
- host = Refile.host || request.base_url
34
- backend_name = Refile.backends.key(cache)
35
-
36
- options[:data] ||= {}
37
- options[:data][:direct] = true
38
- options[:data][:as] = "file"
39
- options[:data][:url] = File.join(host, refile_app_path, backend_name)
40
- end
41
-
42
- if options[:presigned] and cache.respond_to?(:presign)
43
- signature = cache.presign
44
- options[:data] ||= {}
45
- options[:data][:direct] = true
46
- options[:data][:id] = signature.id
47
- options[:data][:url] = signature.url
48
- options[:data][:fields] = signature.fields
49
- options[:data][:as] = signature.as
50
- end
51
- end
52
- hidden_field(object_name, :"#{method}_cache_id", options.slice(:object)) +
53
- file_field(object_name, method, options)
54
- end
55
- end