attachs 0.3.5 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/MIT-LICENSE +1 -1
  3. data/README.md +254 -0
  4. data/Rakefile +0 -2
  5. data/lib/attachs.rb +37 -10
  6. data/lib/attachs/active_record/base.rb +58 -95
  7. data/lib/attachs/active_record/connection_adapters.rb +40 -0
  8. data/lib/attachs/active_record/migration.rb +17 -0
  9. data/lib/attachs/active_record/validators.rb +7 -0
  10. data/lib/attachs/active_record/validators/attachment_content_type_validator.rb +30 -0
  11. data/lib/attachs/active_record/validators/attachment_presence_validator.rb +22 -0
  12. data/lib/attachs/active_record/validators/attachment_size_validator.rb +39 -0
  13. data/lib/attachs/attachment.rb +114 -0
  14. data/{config → lib/attachs}/locales/en.yml +2 -1
  15. data/{config → lib/attachs}/locales/es.yml +2 -1
  16. data/lib/attachs/railtie.rb +31 -11
  17. data/lib/attachs/storages/base.rb +68 -0
  18. data/lib/attachs/storages/local.rb +61 -37
  19. data/lib/attachs/storages/s3.rb +66 -57
  20. data/lib/attachs/tools/magick.rb +52 -0
  21. data/lib/attachs/types/base.rb +22 -0
  22. data/lib/attachs/types/default.rb +21 -0
  23. data/lib/attachs/types/regular.rb +21 -0
  24. data/lib/attachs/version.rb +1 -1
  25. data/lib/generators/attachs/install_generator.rb +13 -0
  26. data/lib/generators/attachs/templates/attachs.rb +2 -0
  27. data/lib/tasks/attachs.rake +30 -0
  28. data/test/attachment_test.rb +19 -0
  29. data/test/dummy/app/models/medium.rb +5 -0
  30. data/test/dummy/app/models/picture.rb +2 -0
  31. data/test/dummy/config/application.rb +1 -7
  32. data/test/dummy/config/initializers/attachs.rb +25 -0
  33. data/test/dummy/config/initializers/secret_token.rb +1 -0
  34. data/test/dummy/config/routes.rb +0 -1
  35. data/test/dummy/db/migrate/20140808012639_create_media.rb +11 -0
  36. data/test/dummy/db/schema.rb +14 -25
  37. data/test/dummy/log/development.log +9 -27
  38. data/test/dummy/log/test.log +52130 -11207
  39. data/test/dummy/test/fixtures/180x150.gif +0 -0
  40. data/test/generator_test.rb +18 -0
  41. data/test/local_storage_test.rb +57 -0
  42. data/test/magick_tool_test.rb +42 -0
  43. data/test/migration_test.rb +65 -0
  44. data/test/s3_storage_tes.rb +56 -0
  45. data/test/tasks_test.rb +61 -0
  46. data/test/test_helper.rb +17 -17
  47. data/test/validators_test.rb +100 -0
  48. metadata +85 -120
  49. data/README.rdoc +0 -104
  50. data/app/controllers/attachs/presets_controller.rb +0 -17
  51. data/config/routes.rb +0 -5
  52. data/lib/attachs/engine.rb +0 -7
  53. data/lib/attachs/magick/image.rb +0 -85
  54. data/lib/attachs/types/file.rb +0 -131
  55. data/lib/attachs/types/image.rb +0 -58
  56. data/lib/attachs/validators/attachment_content_type_validator.rb +0 -11
  57. data/lib/attachs/validators/attachment_presence_validator.rb +0 -9
  58. data/lib/attachs/validators/attachment_size_validator.rb +0 -21
  59. data/lib/attachs/validators/base.rb +0 -13
  60. data/lib/generators/attachs/s3/config_generator.rb +0 -13
  61. data/lib/generators/attachs/s3/templates/s3.yml +0 -16
  62. data/lib/tasks/attachs_tasks.rake +0 -71
  63. data/test/dummy/app/models/all_attached.rb +0 -16
  64. data/test/dummy/app/models/file_attached.rb +0 -3
  65. data/test/dummy/app/models/image_attached.rb +0 -3
  66. data/test/dummy/app/models/private_file_attached.rb +0 -3
  67. data/test/dummy/app/models/private_image_attached.rb +0 -3
  68. data/test/dummy/db/migrate/20130820222342_create_file_attacheds.rb +0 -9
  69. data/test/dummy/db/migrate/20130820222355_create_image_attacheds.rb +0 -9
  70. data/test/dummy/db/migrate/20130820222534_create_all_attacheds.rb +0 -19
  71. data/test/dummy/public/uploads/files/file.txt +0 -1
  72. data/test/dummy/public/uploads/images/big/image.jpg +0 -0
  73. data/test/dummy/public/uploads/images/original/image.jpg +0 -0
  74. data/test/dummy/public/uploads/images/small/image.jpg +0 -0
  75. data/test/dummy/test/fixtures/file_big.txt +0 -1
  76. data/test/dummy/test/fixtures/file_empty.txt +0 -1
  77. data/test/dummy/test/fixtures/image.jpg +0 -0
  78. data/test/dummy/test/fixtures/image_big.jpg +0 -0
  79. data/test/dummy/test/fixtures/image_empty.jpg +0 -0
  80. data/test/dummy/tmp/uploads/files/13898788260004868.txt +0 -1
  81. data/test/local_file_record_test.rb +0 -31
  82. data/test/local_file_string_test.rb +0 -23
  83. data/test/local_file_upload_test.rb +0 -30
  84. data/test/local_generate_test.rb +0 -25
  85. data/test/local_image_record_test.rb +0 -25
  86. data/test/local_image_string_test.rb +0 -34
  87. data/test/local_image_upload_test.rb +0 -27
  88. data/test/local_records_test.rb +0 -52
  89. data/test/local_validators_test.rb +0 -71
  90. data/test/s3_file_record_tes.rb +0 -21
  91. data/test/s3_file_string_tes.rb +0 -24
  92. data/test/s3_file_upload_tes.rb +0 -31
  93. data/test/s3_image_record_tes.rb +0 -26
  94. data/test/s3_image_string_tes.rb +0 -31
  95. data/test/s3_image_upload_tes.rb +0 -28
  96. data/test/s3_records_tes.rb +0 -56
  97. data/test/s3_validators_tes.rb +0 -72
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d4b7e169e9f30ad2bf78cb7f234ff71814200ad6
4
- data.tar.gz: b8a1b2b599f222bf759809834c9e492389629ef0
3
+ metadata.gz: 174763ef00e837f637a02dadcf2c6e732304c911
4
+ data.tar.gz: 543f20a4cfbc0097daa613ec66bf31459191c260
5
5
  SHA512:
6
- metadata.gz: 2ab3ab663652618fbe9bf500def726a1f101bbb695d7a7bd6ba30b57963fe1b4d2f6b3892d974764b850c53c5f9b0c08b6903acdd782a6e8ad3e258372a27c02
7
- data.tar.gz: 45bf708582744d4c012e5b739f921a3c1fc72e4ec2824516dbf9dfa0be08d185956e952f7159f9f25755ca977a343534e3854567be38a598ab8ffaf50e68eb20
6
+ metadata.gz: 5fc324cf2d3df019d8d37501e9af50eb1169d3e88b6ba11c6be97afdfd22377d3dd8391e03fb170d051f852b1116da68ce195de93d22bab1f98edf5986bee561
7
+ data.tar.gz: a57ec77ecc61acebed6512bce22a18c0152fc26b9f488f86daeed88f055e44b4284388644bba3ef7500eeecb7f99469e86f94435a356c627b1f77f99800f4c94
data/MIT-LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright 2013 Mattways
1
+ Copyright 2014 Museways
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md ADDED
@@ -0,0 +1,254 @@
1
+ [![Gem Version](https://badge.fury.io/rb/attachs.svg)](http://badge.fury.io/rb/attachs) [![Code Climate](https://codeclimate.com/github/museways/attachs/badges/gpa.svg)](https://codeclimate.com/github/museways/attachs) [![Build Status](https://travis-ci.org/museways/attachs.svg?branch=master)](https://travis-ci.org/museways/attachs)
2
+
3
+ # Attachs
4
+
5
+ Minimalistic toolkit to attach files to records.
6
+
7
+ ## Install
8
+
9
+ Put this line in your Gemfile:
10
+ ```ruby
11
+ gem 'attachs'
12
+ ```
13
+
14
+ Then bundle:
15
+ ```
16
+ $ bundle
17
+ ```
18
+
19
+ NOTE: ImageMagick is needed.
20
+
21
+ ## Configuration
22
+
23
+ Generate the configuration file:
24
+ ```
25
+ rails g attachs:install
26
+ ```
27
+
28
+ The defaults values are:
29
+ ```ruby
30
+ Attachs.configure do |config|
31
+ config.styles = {}
32
+ config.interpolations = {}
33
+ config.global_styles = []
34
+ config.global_convert_options= ''
35
+ config.convert_options = {}
36
+ config.default_storage = :local
37
+ config.default_path = '/:timestamp-:filename'
38
+ config.base_url = ''
39
+ config.s3 = { ssl: false }
40
+ end
41
+ ```
42
+
43
+ ## Usage
44
+
45
+ Add the column to your table:
46
+ ```ruby
47
+ create_table :users do |t|
48
+ t.attachment :avatar
49
+ end
50
+ ```
51
+
52
+ Define the attachment in your model:
53
+ ```ruby
54
+ class User < ActiveRecord::Base
55
+ has_attached_file :avatar
56
+ end
57
+ ```
58
+
59
+ ## Paths
60
+
61
+ To customize the path to some model:
62
+ ```ruby
63
+ class User < ActiveRecord::Base
64
+ has_attached_file :avatar, path: '/:type/:timestamp/:filename'
65
+ end
66
+ ```
67
+
68
+ To create custom interpolations:
69
+ ```ruby
70
+ Attachs.configure do |config|
71
+ config.interpolations = {
72
+ category: -> (attachment) { attachment.record.category }
73
+ }
74
+ end
75
+ ```
76
+
77
+ NOTE: Look into lib/attachs/storages/base.rb to find a list of the system interpolations.
78
+
79
+ ## Styles
80
+
81
+ Define the styles of the attachment:
82
+ ```ruby
83
+ Attachs.configure do |config|
84
+ config.styles = {
85
+ small: '120x120!', # forces the exact size
86
+ medium: '240x240#', # resizes and crops to fill the desire space
87
+ big: '360x360' # resizes to contain the imagen in the desire space
88
+ }
89
+ end
90
+ ```
91
+
92
+ Then reference them in your model:
93
+ ```ruby
94
+ class User < ActiveRecord::Base
95
+ has_attached_file :avatar, styles: [:small, :medium]
96
+ end
97
+ ```
98
+
99
+ To set styles for all attachments:
100
+ ```ruby
101
+ Attachs.configure do |config|
102
+ config.global_styles = [:big]
103
+ end
104
+ ```
105
+
106
+ ## Convert
107
+
108
+ To define global convert options:
109
+ ```ruby
110
+ Attachs.configure do |config|
111
+ config.global_convert_options = '-quality 75 -strip'
112
+ end
113
+ ```
114
+
115
+ To set convert options to some styles only:
116
+ ```ruby
117
+ Attachs.configure do |config|
118
+ config.convert_options = {
119
+ medium: '-trim'
120
+ }
121
+ end
122
+ ```
123
+
124
+ ## Security
125
+
126
+ To make the attachment private:
127
+ ```ruby
128
+ class User < ActiveRecord::Base
129
+ has_attached_file :avatar, private: true
130
+ end
131
+ ```
132
+
133
+ NOTE: Private attachments will be saved into /private folder.
134
+
135
+ ## Validations
136
+
137
+ To validate the presence of the attachment:
138
+ ```ruby
139
+ class User < ActiveRecord::Base
140
+ has_attached_file :avatar
141
+ validates_attachment_presence_of :avatar
142
+ end
143
+ ```
144
+
145
+ To validate the size of the attachment:
146
+ ```ruby
147
+ class User < ActiveRecord::Base
148
+ has_attached_file :avatar
149
+ validates_attachment_size_of :avatar, in: 1..5.megabytes
150
+ end
151
+ ```
152
+
153
+ To validate the content type of the attachment:
154
+ ```ruby
155
+ class User < ActiveRecord::Base
156
+ has_attached_file :avatar
157
+ validates_attachment_content_type_of :avatar, with: /\Aimage/
158
+ # Or using a list
159
+ validates_attachment_content_type_of :avatar, in: %w(image/jpg image/png)
160
+ end
161
+ ```
162
+
163
+ ## I18n
164
+
165
+ To translate the messages the keys are:
166
+ ```
167
+ errors.messages.attachment_presence
168
+ errors.messages.attachment_size_in
169
+ errors.messages.attachment_size_less_than
170
+ errors.messages.attachment_size_greater_than
171
+ errors.messages.attachment_content_type_with
172
+ errors.messages.attachment_content_type_in
173
+ ```
174
+
175
+ NOTE: Look into lib/attachs/locales yamls.
176
+
177
+ ## Forms
178
+
179
+ Your forms continue to work the same:
180
+ ```erb
181
+ <%= form_for @user do |f| %>
182
+ <%= f.file_field :avatar %>
183
+ <% end %>
184
+ ```
185
+
186
+ ## Urls
187
+
188
+ The url method points to the original file:
189
+ ```erb
190
+ <%= image_tag user.avatar.url %>
191
+ ```
192
+
193
+ To point to some particular style:
194
+ ```erb
195
+ <%= image_tag user.avatar.url(:small) %>
196
+ ```
197
+
198
+ The defauft url is used when there is no upload:
199
+ ```ruby
200
+ class User < ActiveRecord::Base
201
+ has_attached_file :avatar, default_url: '/missing.png'
202
+ end
203
+ ```
204
+
205
+ NOTE: If storage is s3 you can pass ssl: true to force https.
206
+
207
+ ## Storage
208
+
209
+ To override the storage in the model:
210
+ ```ruby
211
+ class User < ActiveRecord::Base
212
+ has_attached_file :avatar, storage: :s3
213
+ end
214
+ ```
215
+
216
+ To configure the s3 credentials:
217
+ ```ruby
218
+ Attachs.configure do |config|
219
+ config.s3 = {
220
+ bucket: 'xxx',
221
+ access_key_id: 'xxx',
222
+ secret_access_key: 'xxx'
223
+ }
224
+ end
225
+ ```
226
+
227
+ ## CDN
228
+
229
+ To configure a cdn:
230
+ ```ruby
231
+ Attachs.configure do |config|
232
+ config.base_url = 'http://cdn.example.com'
233
+ end
234
+ ```
235
+
236
+ ## Tasks
237
+
238
+ To refresh all the styles of some attachment:
239
+ ```
240
+ bundle exec rake attachs:refresh:all CLASS=User ATTACHMENT=avatar
241
+ ```
242
+
243
+ To refresh missing styles of some attachment:
244
+ ```
245
+ bundle exec rake attachs:refresh:missing CLASS=User ATTACHMENT=avatar
246
+ ```
247
+
248
+ ## Credits
249
+
250
+ This gem is maintained and funded by [museways](http://museways.com).
251
+
252
+ ## License
253
+
254
+ It is free software, and may be redistributed under the terms specified in the MIT-LICENSE file.
data/Rakefile CHANGED
@@ -14,8 +14,6 @@ RDoc::Task.new(:rdoc) do |rdoc|
14
14
  rdoc.rdoc_files.include('lib/**/*.rb')
15
15
  end
16
16
 
17
- APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
18
- load 'rails/tasks/engine.rake'
19
17
 
20
18
 
21
19
 
data/lib/attachs.rb CHANGED
@@ -1,16 +1,43 @@
1
- require 'attachs/active_record/base'
2
- require 'attachs/magick/image'
1
+ require 'open3'
2
+ require 'attachs/attachment'
3
+ require 'attachs/tools/magick'
4
+ require 'attachs/storages/base'
3
5
  require 'attachs/storages/local'
4
6
  require 'attachs/storages/s3'
5
- require 'attachs/types/file'
6
- require 'attachs/types/image'
7
- require 'attachs/validators/base'
8
- require 'attachs/validators/attachment_content_type_validator'
9
- require 'attachs/validators/attachment_presence_validator'
10
- require 'attachs/validators/attachment_size_validator'
11
- require 'attachs/engine'
7
+ require 'attachs/types/base'
8
+ require 'attachs/types/default'
9
+ require 'attachs/types/regular'
10
+ require 'attachs/active_record/base'
11
+ require 'attachs/active_record/connection_adapters'
12
+ require 'attachs/active_record/migration'
13
+ require 'attachs/active_record/validators'
14
+ require 'attachs/active_record/validators/attachment_content_type_validator'
15
+ require 'attachs/active_record/validators/attachment_presence_validator'
16
+ require 'attachs/active_record/validators/attachment_size_validator'
12
17
  require 'attachs/railtie'
13
- require 'attachs/version'
14
18
 
15
19
  module Attachs
20
+ class << self
21
+
22
+ def configure
23
+ yield config
24
+ end
25
+
26
+ def config
27
+ @config ||= begin
28
+ ActiveSupport::OrderedOptions.new.tap do |config|
29
+ config.styles = {}
30
+ config.interpolations = {}
31
+ config.global_styles = []
32
+ config.global_convert_options= ''
33
+ config.convert_options = {}
34
+ config.default_storage = :local
35
+ config.default_path = '/:timestamp-:filename'
36
+ config.base_url = ''
37
+ config.s3 = { ssl: false }
38
+ end
39
+ end
40
+ end
41
+
42
+ end
16
43
  end
@@ -5,136 +5,99 @@ module Attachs
5
5
 
6
6
  protected
7
7
 
8
- attr_reader :stored_attachments, :deleted_attachments
9
-
10
8
  def attachments
11
- @attachments ||= {}
12
- end
13
-
14
- def add_attachment(attr, value, options)
15
- attachment = build_attachment_instance(value, options)
16
- attachments[attr] = attachment
17
- write_attribute attr, attachment.filename
9
+ @attachments ||= {}.tap do |instances|
10
+ self.class.attachments.each do |attr, options|
11
+ instances[attr] = Attachs::Attachment.new(self, attr, options)
12
+ end
13
+ end
18
14
  end
19
15
 
20
- def get_attachment(attr, options)
21
- return attachments[attr] if attachments.has_key? attr
22
- return nil unless read_attribute(attr) or options.has_key? :default
23
- attachments[attr] = build_attachment_instance(read_attribute(attr), options)
16
+ def queued_attachments
17
+ @queued_attachments ||= { process: {}, destroy: {} }
24
18
  end
25
19
 
26
- def remove_attachment(attr)
27
- write_attribute attr, nil
20
+ def queue_attachment(attr, options, value)
21
+ unless queued_attachments[:destroy].has_key? attr
22
+ queued_attachments[:destroy][attr] = attachments[attr]
23
+ end
24
+ if queued_attachments[:process].has_key? attr and value.nil?
25
+ attachments[attr]
26
+ else
27
+ queued_attachments[:process][attr] = Attachs::Attachment.new(self, attr, options, value)
28
+ end
28
29
  end
29
30
 
30
- def build_attachment_instance(source, options)
31
- klass = options.has_key?(:type) ? options[:type].to_s.classify : 'File'
32
- Attachs::Types.const_get(klass).new source, options
33
- end
34
-
35
- def check_changed_attachments
36
- @stored_attachments = []
37
- @deleted_attachments = []
31
+ def commit_attachments
38
32
  self.class.attachments.each do |attr, options|
39
- if changed_attributes.has_key? attr.to_s
40
- stored = attributes[attr.to_s]
41
- deleted = changed_attributes[attr.to_s]
42
- add_changed_attachment stored, options, :stored if stored.present?
43
- add_changed_attachment deleted, options, :deleted if deleted.present?
44
- end
33
+ send "destroy_#{attr}=", nil
45
34
  end
46
- end
47
-
48
- def iterate_attachments
49
- self.class.attachments.each do |attr, options|
50
- next unless instance = send(attr)
51
- yield instance
35
+ queued_attachments[:destroy].each do |attr, attachment|
36
+ attachment.destroy
52
37
  end
53
- end
54
-
55
- def add_changed_attachment(source, options, type)
56
- (type == :stored ? stored_attachments : deleted_attachments) << (source.is_a?(String) ? build_attachment_instance(source, options) : source)
57
- end
58
-
59
- def store_attachments
60
- iterate_attachments { |a| a.store }
38
+ queued_attachments[:process].each do |attr, attachment|
39
+ attachment.process
40
+ end
41
+ queued_attachments[:destroy] = {}
42
+ queued_attachments[:process] = {}
61
43
  end
62
44
 
63
- def delete_attachments
64
- iterate_attachments { |a| a.delete }
65
- end
66
-
67
- def remove_stored_attachments
68
- stored_attachments.each { |a| a.delete }
45
+ def destroy_attachments
46
+ self.class.attachments.each do |attr, options|
47
+ send(attr).destroy
48
+ end
69
49
  end
70
-
71
- def remove_deleted_attachments
72
- deleted_attachments.each { |a| a.delete }
73
- end
74
50
 
75
51
  module ClassMethods
76
52
 
77
- [:file, :image].each do |type|
78
- name = :"has_attached_#{type}"
79
- define_method name do |*args|
80
- options = args.extract_options!
81
- options[:type] = type
82
- define_attachment *args.map(&:to_sym).append(options)
53
+ def has_attached_file(*args)
54
+ options = args.extract_options!
55
+ unless attachable?
56
+ make_attachable
57
+ end
58
+ args.each do |attr|
59
+ define_attachment_methods attr, options
60
+ attachments[attr] = options
83
61
  end
84
- alias_method :"attached_#{type}", name
85
62
  end
86
63
 
87
- attr_reader :attachments
88
-
89
- def inherited(subclass)
90
- subclass.instance_variable_set :@attachments, @attachments
91
- super
64
+ def attachments
65
+ @attachments ||= {}
92
66
  end
93
67
 
94
68
  def attachable?
95
- attachments.present?
69
+ attachments.any?
96
70
  end
97
71
 
98
- protected
99
-
100
- def define_attachment(*args)
101
- options = args.extract_options!
102
- make_attachable unless attachable?
103
- args.each do |attr|
104
- define_attachment_attribute_methods attr, options
105
- define_attachment_default_validations attr, options
106
- attachments[attr] = options
107
- end
72
+ def inherited(subclass)
73
+ subclass.instance_variable_set :@attachments, @attachments
74
+ super
108
75
  end
109
76
 
77
+ protected
78
+
110
79
  def make_attachable
111
- before_save :store_attachments, :check_changed_attachments
112
- after_save :remove_deleted_attachments
113
- before_destroy :delete_attachments
114
- after_rollback :remove_stored_attachments
115
- @attachments = {}
80
+ after_save :commit_attachments
81
+ after_destroy :destroy_attachments
116
82
  end
117
83
 
118
- def define_attachment_attribute_methods(attr, options)
84
+ def define_attachment_methods(attr, options)
119
85
  define_method "#{attr}=" do |value|
120
- add_attachment attr, value, options if value.is_a? ActionDispatch::Http::UploadedFile or value.is_a? Rack::Test::UploadedFile
86
+ attachments[attr] = queue_attachment(attr, options, value)
121
87
  end
122
88
  define_method attr do
123
- get_attachment attr, options
124
- end
125
- define_method "delete_#{attr}=" do |value|
126
- remove_attachment attr if value == '1'
127
- instance_variable_set :"@delete_#{attr}", value
89
+ attachments[attr]
90
+ end
91
+ attr_reader "destroy_#{attr}"
92
+ define_method "destroy_#{attr}=" do |value|
93
+ instance_variable_set "@destroy_#{attr}", value
94
+ if [1, '1', true].include?(value) and !send(attr).upload?
95
+ send "#{attr}=", nil
96
+ end
128
97
  end
129
- attr_reader :"delete_#{attr}"
130
- end
131
-
132
- def define_attachment_default_validations(attr, options)
133
- default_validations = Rails.application.config.attachs.default_validations[options[:type]]
134
- validates attr, default_validations.dup if default_validations.present?
135
98
  end
136
99
 
137
- end
100
+ end
138
101
  end
139
102
  end
140
103
  end