paperweight 0.0.5 → 0.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8380f20790cf3d11b41e3da6274c68fc6c0ac692fcaeb2a5aa7c2c2641bebeff
4
- data.tar.gz: 9d992fc125eefcf29f06cd590f4e742bda84e1fc2ab47d08e8b113ac2d77d1bc
3
+ metadata.gz: c257b89200e21b312d6e80618546a84263d2f4d87408d07e2ae1d9bb86b2935b
4
+ data.tar.gz: 30997e5c1c095e24ba48ee655c8ca7f487a741c0687858d9e991c75730c3cb90
5
5
  SHA512:
6
- metadata.gz: ee438611f9a2788d970f41eb5c1cdf6c7b75ac47325a0aa603bb7a88e48a788fbbde058941cb14dcc22b1038a21c2dcc6c7151ea33498387d587c3f18eba2597
7
- data.tar.gz: '0568df48aa2c36ef68f8b24a7e04f6ec41e8ead419ceb4a3524831742236af9c718b9414096eb93d0f99602fb51a3fa650192ebeca46003d385228aec3ed4a8c'
6
+ metadata.gz: 1e63cb06b6c8257719e098666b220f04499d119e0dfa016c9054e8802d742933053bc6935c5f2b19084b4e84d9d8973a1586e80070a5e794b446ef81f3dd8648
7
+ data.tar.gz: 50048131ed8ad3430c886e2fc633730b3351777131bcac59b9b78e5ebb16c5e96ffcda5e72f148d078140d9e5cefa0176963ecbe5a4fd58199ad04fef2d5d57d
data/.gitignore CHANGED
@@ -5,5 +5,6 @@
5
5
  /doc/
6
6
  /log/
7
7
  /pkg/
8
+ /public/
8
9
  /spec/reports/
9
10
  /tmp/
data/Gemfile.lock CHANGED
@@ -1,8 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- paperweight (0.0.5)
5
- aws-sdk-s3 (~> 1.13)
4
+ paperweight (0.1.0)
6
5
  paperclip (~> 6.0)
7
6
  rails (~> 5.2)
8
7
 
@@ -52,21 +51,6 @@ GEM
52
51
  tzinfo (~> 1.1)
53
52
  arel (9.0.0)
54
53
  ast (2.4.0)
55
- aws-eventstream (1.0.0)
56
- aws-partitions (1.91.0)
57
- aws-sdk-core (3.21.2)
58
- aws-eventstream (~> 1.0)
59
- aws-partitions (~> 1.0)
60
- aws-sigv4 (~> 1.0)
61
- jmespath (~> 1.0)
62
- aws-sdk-kms (1.5.0)
63
- aws-sdk-core (~> 3)
64
- aws-sigv4 (~> 1.0)
65
- aws-sdk-s3 (1.14.0)
66
- aws-sdk-core (~> 3, >= 3.21.2)
67
- aws-sdk-kms (~> 1)
68
- aws-sigv4 (~> 1.0)
69
- aws-sigv4 (1.0.2)
70
54
  builder (3.2.3)
71
55
  climate_control (0.2.0)
72
56
  concurrent-ruby (1.0.5)
@@ -78,7 +62,6 @@ GEM
78
62
  i18n (1.0.1)
79
63
  concurrent-ruby (~> 1.0)
80
64
  jaro_winkler (1.5.1)
81
- jmespath (1.4.0)
82
65
  json (2.1.0)
83
66
  loofah (2.2.2)
84
67
  crass (~> 1.0.2)
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![Build Status](https://travis-ci.com/CultureHQ/paperweight.svg?branch=master)](https://travis-ci.com/CultureHQ/paperweight)
4
4
 
5
- An opinionated Paperclip for a specific workflow. Only accepts image URLs instead of uploaded files. The image URLs are then queued for downloading, converting, and generating thumbnails in the background using `ActiveJob`.
5
+ Handles `Paperclip` attachments on the backend in a delayed process.
6
6
 
7
7
  ## Installation
8
8
 
@@ -22,45 +22,7 @@ Or install it yourself as:
22
22
 
23
23
  ## Usage
24
24
 
25
- First, configure `paperweight` in an initializer, e.g., `config/initializers/paperweight.rb`:
26
-
27
- ```ruby
28
- Paperweight.configure do |config|
29
- # your asset server (preferably a CDN)
30
- config.asset_server = 'https://uploads.culturehq.com'
31
-
32
- # the S3 bucket to which to uploads images
33
- config.bucket = 'culturehq-uploads'
34
-
35
- # the AWS credentials used with permission to S3
36
- config.credentials = {
37
- access_key_id: Rails.application.credentials.aws_access_key_id,
38
- secret_access_key: Rails.application.credentials.aws_secret_access_key,
39
- region: 'us-west-2'
40
- }
41
- end
42
- ```
43
-
44
- Then, create and run a migration to add the `image_uuid` and `image_processing` columns to your model:
45
-
46
- ```ruby
47
- class AddImageToPost < ActiveRecord::Migration[5.2]
48
- def change
49
- add_column :posts, :image_uuid, :string
50
- add_column :posts, :image_processing, :boolean, default: false, null: false
51
- end
52
- end
53
- ```
54
-
55
- Next, add the class macro to the model:
56
-
57
- ```ruby
58
- class Post < ApplicationRecord
59
- has_image thumb: '100x100>'
60
- end
61
- ```
62
-
63
- Finally, add the ability to update the `image_url` attribute from the controller:
25
+ Configure `Paperclip` as you normally would, according to their docs. Then, add the ability to update the `*_url` attribute from the controller, where the `*` is the name of the attachment that you specified in your model's call to `has_attached_file`.
64
26
 
65
27
  ```ruby
66
28
  class PostsController < ApplicationController
@@ -83,6 +45,8 @@ class PostsController < ApplicationController
83
45
  end
84
46
  ```
85
47
 
48
+ From now on, updating this attribute will queue a job in the background to update the image later.
49
+
86
50
  ## Development
87
51
 
88
52
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -32,12 +32,6 @@ module Paperweight
32
32
  raise Error, "download failed (#{url}): #{message}"
33
33
  end
34
34
 
35
- def self.download(url)
36
- new.download(url)
37
- rescue Error
38
- nil
39
- end
40
-
41
35
  private
42
36
 
43
37
  # open-uri will return a StringIO instead of a Tempfile if the filesize
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Paperweight
4
+ # Overrides the `has_attached_file` method from `paperclip` so that
5
+ # `paperweight` can add extras when the macro is called.
6
+ module Hook
7
+ # Converts a parent name to its respective component child names.
8
+ class AttachmentName
9
+ attr_reader :name
10
+
11
+ def initialize(name)
12
+ @name = name
13
+ end
14
+
15
+ def processing
16
+ :"#{name}_processing"
17
+ end
18
+
19
+ def url
20
+ :"#{name}_url"
21
+ end
22
+
23
+ def url_eq
24
+ :"#{name}_url="
25
+ end
26
+
27
+ def url_attr
28
+ :"@#{name}_url"
29
+ end
30
+ end
31
+
32
+ def has_attached_file(name, *) # rubocop:disable Naming/PredicateName
33
+ super
34
+
35
+ name = AttachmentName.new(name)
36
+ attr_reader name.url
37
+
38
+ define_paperweight_setter_for(name)
39
+ define_paperweight_after_commit_for(name)
40
+ end
41
+
42
+ private
43
+
44
+ def define_paperweight_setter_for(name)
45
+ define_method(name.url_eq) do |value|
46
+ instance_variable_set(name.url_attr, value)
47
+ self[name.processing] = value ? true : false
48
+ end
49
+ end
50
+
51
+ def define_paperweight_after_commit_for(name)
52
+ after_commit if: name.url do
53
+ attachment_url = public_send(name.url)
54
+ PostProcessJob.perform_later(self, name.name.to_s, attachment_url)
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Paperweight
4
+ # Queues post processing.
5
+ class PostProcessJob < ActiveJob::Base
6
+ queue_as :default
7
+
8
+ discard_on ActiveJob::DeserializationError
9
+
10
+ def perform(model, name, url)
11
+ model.update!(
12
+ name => Download.new.download(url),
13
+ :"#{name}_processing" => false
14
+ )
15
+ end
16
+ end
17
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Paperweight
4
- VERSION = '0.0.5'
4
+ VERSION = '0.1.0'
5
5
  end
data/lib/paperweight.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'aws-sdk-s3'
4
3
  require 'open-uri'
5
4
  require 'net/http'
6
5
  require 'paperclip'
@@ -8,12 +7,9 @@ require 'rails'
8
7
 
9
8
  require 'active_job'
10
9
 
11
- require 'paperweight/config'
12
10
  require 'paperweight/download'
13
- require 'paperweight/image'
14
- require 'paperweight/location'
15
- require 'paperweight/purge_job'
16
- require 'paperweight/railtie'
17
- require 'paperweight/storage'
18
- require 'paperweight/thumbnails_job'
11
+ require 'paperweight/hook'
12
+ require 'paperweight/post_process_job'
19
13
  require 'paperweight/version'
14
+
15
+ Paperclip::ClassMethods.prepend(Paperweight::Hook)
data/paperweight.gemspec CHANGED
@@ -21,7 +21,6 @@ Gem::Specification.new do |spec|
21
21
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
22
  spec.require_paths = ['lib']
23
23
 
24
- spec.add_dependency 'aws-sdk-s3', '~> 1.13'
25
24
  spec.add_dependency 'paperclip', '~> 6.0'
26
25
  spec.add_dependency 'rails', '~> 5.2'
27
26
 
metadata CHANGED
@@ -1,29 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: paperweight
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kevin Deisz
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-06-15 00:00:00.000000000 Z
11
+ date: 2018-06-18 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: aws-sdk-s3
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: '1.13'
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - "~>"
25
- - !ruby/object:Gem::Version
26
- version: '1.13'
27
13
  - !ruby/object:Gem::Dependency
28
14
  name: paperclip
29
15
  requirement: !ruby/object:Gem::Requirement
@@ -154,14 +140,9 @@ files:
154
140
  - bin/console
155
141
  - bin/setup
156
142
  - lib/paperweight.rb
157
- - lib/paperweight/config.rb
158
143
  - lib/paperweight/download.rb
159
- - lib/paperweight/image.rb
160
- - lib/paperweight/location.rb
161
- - lib/paperweight/purge_job.rb
162
- - lib/paperweight/railtie.rb
163
- - lib/paperweight/storage.rb
164
- - lib/paperweight/thumbnails_job.rb
144
+ - lib/paperweight/hook.rb
145
+ - lib/paperweight/post_process_job.rb
165
146
  - lib/paperweight/version.rb
166
147
  - paperweight.gemspec
167
148
  homepage: https://github.com/CultureHQ/paperweight
@@ -1,24 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # An opinionated Paperclip
4
- module Paperweight
5
- # Stores various metadata about the configuration of `paperweight`.
6
- class Config
7
- attr_accessor :asset_server, :bucket, :credentials
8
-
9
- def initialize
10
- @bucket = ''
11
- @credentials = { region: 'us-east-1' }
12
- end
13
- end
14
-
15
- class << self
16
- def config
17
- @config ||= Config.new
18
- end
19
-
20
- def configure
21
- yield config
22
- end
23
- end
24
- end
@@ -1,61 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Paperweight
4
- # The image object attached to the model.
5
- class Image
6
- attr_reader :model
7
-
8
- def initialize(model)
9
- @model = model
10
- end
11
-
12
- def as_json(*)
13
- serialized_styles_for(model).merge!(
14
- is_default: model.image? ? false : true,
15
- processing: model.image_processing?
16
- )
17
- end
18
-
19
- def update_file(file)
20
- image_styles.each do |style, geometry|
21
- options = { geometry: geometry, convert_options: '-strip' }
22
- thumbnail = Paperclip::Thumbnail.new(file, options).make
23
- Storage.adapter.put(thumbnail, Location.new(model, style).path)
24
- end
25
-
26
- model.clear_image_url
27
- model.update!(image_processing: false)
28
- end
29
-
30
- def update_url(url)
31
- update_file(Download.download(url))
32
- end
33
-
34
- def path(style = :original)
35
- Location.new(model, style).path
36
- end
37
-
38
- def url(style = :original)
39
- Location.new(model, style).url
40
- end
41
-
42
- private
43
-
44
- def image_styles
45
- model.class.image_styles
46
- end
47
-
48
- def serialized_styles_for(model)
49
- url_method = url_method_for(model)
50
-
51
- image_styles.each_key.each_with_object({}) do |style, serialized|
52
- serialized[:"#{style}_url"] =
53
- Location.new(model, style).public_send(url_method)
54
- end
55
- end
56
-
57
- def url_method_for(model)
58
- !model.image? || model.image_processing? ? :default_url : :url
59
- end
60
- end
61
- end
@@ -1,40 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Paperweight
4
- # The location of the attachment in terms of paths are URLs.
5
- class Location
6
- attr_reader :model, :style
7
-
8
- def initialize(model, style = :original)
9
- @model = model
10
- @style = style
11
- end
12
-
13
- def path
14
- "#{table_name}/#{model.id}-#{style}-#{model.image_uuid}"
15
- end
16
-
17
- def url
18
- "#{self.class.prefix}/#{path}"
19
- end
20
-
21
- def default_url
22
- "#{self.class.prefix}/#{table_name}/#{style}.png"
23
- end
24
-
25
- def self.prefix
26
- @prefix ||=
27
- if Rails.env.production?
28
- Paperweight.config.asset_server
29
- else
30
- 'http://localhost:3000/paperweight'
31
- end
32
- end
33
-
34
- private
35
-
36
- def table_name
37
- model.class.table_name
38
- end
39
- end
40
- end
@@ -1,25 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Paperweight
4
- # Removes all previous image files matching the given pattern.
5
- class PurgeJob < ActiveJob::Base
6
- PLACEHOLDER = ':style'
7
-
8
- queue_as :default
9
-
10
- def perform(pattern, styles)
11
- styles.each do |style|
12
- Storage.adapter.delete(pattern.gsub(PLACEHOLDER, style))
13
- end
14
- end
15
-
16
- def self.perform_later_for(model, image_uuid = nil)
17
- model.image_uuid = image_uuid if image_uuid
18
-
19
- perform_later(
20
- Location.new(model, PLACEHOLDER).path,
21
- model.class.image_styles.keys.map(&:to_s)
22
- )
23
- end
24
- end
25
- end
@@ -1,70 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Paperweight
4
- # Adds the `paperweight` hook in ActiveRecord models.
5
- class Railtie < Rails::Railtie
6
- # Extensions that need be generated from input.
7
- class DynamicExtension < Module
8
- def initialize(styles)
9
- define_method(:image_styles) { styles }
10
- end
11
- end
12
-
13
- # Extensions that are the same for each kind of attachment.
14
- module StaticExtension
15
- def clear_image_url
16
- @image_url = nil
17
- end
18
-
19
- def image
20
- Image.new(self)
21
- end
22
-
23
- def image?
24
- image_uuid
25
- end
26
-
27
- def image_url
28
- @image_url
29
- end
30
-
31
- def image_url=(url)
32
- assign_attributes(
33
- image_processing: url ? true : false,
34
- image_uuid: url ? SecureRandom.uuid : nil
35
- )
36
-
37
- @image_url = url
38
- end
39
-
40
- def self.included(base)
41
- base.after_commit do
42
- PurgeJob.perform_later_for(self, prev_image_uuid) if prev_image_uuid
43
- ThumbnailsJob.perform_later(self, image_url) if image_url
44
- end
45
-
46
- base.before_destroy if: :image? do
47
- PurgeJob.perform_later_for(self)
48
- end
49
- end
50
-
51
- private
52
-
53
- def prev_image_uuid
54
- previous_changes.fetch('image_uuid', [])[0]
55
- end
56
- end
57
-
58
- # Provides the `has_image` method to attach to models.
59
- module Hook
60
- def has_image(styles = {}) # rubocop:disable Naming/PredicateName
61
- extend DynamicExtension.new({ original: '' }.merge!(styles))
62
- include StaticExtension
63
- end
64
- end
65
-
66
- initializer 'paperweight' do
67
- ActiveSupport.on_load(:active_record) { extend Hook }
68
- end
69
- end
70
- end
@@ -1,66 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Paperweight
4
- # Handles putting and deleting files from different kinds of storage.
5
- module Storage
6
- # A storage adapter for Amazon AWS S3.
7
- class Remote
8
- attr_reader :client
9
-
10
- def initialize
11
- @client = ::Aws::S3::Client.new(Paperweight.config.credentials)
12
- end
13
-
14
- def delete(key)
15
- client.delete_object(bucket: Paperweight.config.bucket, key: key)
16
- end
17
-
18
- def put(file, key)
19
- client.put_object(
20
- acl: 'public-read',
21
- bucket: Paperweight.config.bucket,
22
- key: key,
23
- content_type: Paperclip::ContentTypeDetector.new(file.path).detect,
24
- content_disposition: 'attachment',
25
- body: file
26
- )
27
- end
28
- end
29
-
30
- # A storage adapter for a local filesystem.
31
- class Local
32
- attr_reader :root
33
-
34
- def initialize(root)
35
- @root = root
36
- end
37
-
38
- def delete(key)
39
- FileUtils.rm(File.join(root, key))
40
- end
41
-
42
- def put(file, key)
43
- filepath = File.join(root, key)
44
- FileUtils.mkdir_p(File.dirname(filepath))
45
- FileUtils.cp(file.path, filepath)
46
- end
47
- end
48
-
49
- class << self
50
- def adapter
51
- @adapter ||=
52
- case Rails.env
53
- when 'production' then Remote.new
54
- when 'development' then local_for('public')
55
- when 'test' then local_for('tmp')
56
- end
57
- end
58
-
59
- private
60
-
61
- def local_for(directory)
62
- Local.new(Rails.root.join(directory, 'paperweight').to_s.freeze)
63
- end
64
- end
65
- end
66
- end
@@ -1,14 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Paperweight
4
- # Queues thumbnails for processing.
5
- class ThumbnailsJob < ActiveJob::Base
6
- queue_as :default
7
-
8
- discard_on ActiveJob::DeserializationError
9
-
10
- def perform(model, url)
11
- model.image.update_url(url)
12
- end
13
- end
14
- end