mirror of
https://github.com/danbee/my-images
synced 2025-03-04 08:49:05 +00:00
Merge 4d8f516252 into bc1afcf1f0
This commit is contained in:
commit
0c781c5bfa
3
.gitignore
vendored
3
.gitignore
vendored
@ -18,6 +18,9 @@
|
||||
# Ignore environment variables
|
||||
.env
|
||||
|
||||
# Ignore Active Storage
|
||||
/storage
|
||||
|
||||
# Ignore dragonfly uploads
|
||||
/public/system
|
||||
/public/packs
|
||||
|
||||
2
Gemfile
2
Gemfile
@ -7,8 +7,8 @@ gem "dotenv-rails", groups: %i[development test]
|
||||
gem "rails", "5.2.1"
|
||||
|
||||
gem "delayed_job_active_record"
|
||||
gem "dragonfly"
|
||||
gem "http"
|
||||
gem "mini_magick"
|
||||
gem "omniauth-github"
|
||||
gem "pg"
|
||||
gem "puma"
|
||||
|
||||
@ -82,10 +82,6 @@ GEM
|
||||
dotenv-rails (2.5.0)
|
||||
dotenv (= 2.5.0)
|
||||
railties (>= 3.2, < 6.0)
|
||||
dragonfly (1.1.5)
|
||||
addressable (~> 2.3)
|
||||
multi_json (~> 1.0)
|
||||
rack (>= 1.3)
|
||||
erubi (1.7.1)
|
||||
execjs (2.7.0)
|
||||
faraday (0.12.2)
|
||||
@ -121,6 +117,7 @@ GEM
|
||||
mimemagic (~> 0.3.2)
|
||||
method_source (0.9.1)
|
||||
mimemagic (0.3.2)
|
||||
mini_magick (4.9.2)
|
||||
mini_mime (1.0.1)
|
||||
mini_portile2 (2.3.0)
|
||||
minitest (5.11.3)
|
||||
@ -266,10 +263,10 @@ DEPENDENCIES
|
||||
capybara
|
||||
delayed_job_active_record
|
||||
dotenv-rails
|
||||
dragonfly
|
||||
geckodriver-helper
|
||||
http
|
||||
launchy
|
||||
mini_magick
|
||||
omniauth-github
|
||||
pg
|
||||
pry
|
||||
@ -289,4 +286,4 @@ RUBY VERSION
|
||||
ruby 2.5.1p57
|
||||
|
||||
BUNDLED WITH
|
||||
1.16.4
|
||||
1.16.5
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
[ ](https://app.codeship.com/projects/300990)
|
||||
|
||||
A simple DragonFly powered image storage application.
|
||||
A simple Active Storage powered image storage application.
|
||||
|
||||

|
||||
|
||||
|
||||
@ -1,9 +1,15 @@
|
||||
module ImageHelper
|
||||
def image_variant(image, variant)
|
||||
image.variant(variant)
|
||||
end
|
||||
|
||||
def image_thumb(image, size)
|
||||
if image.format == "jpeg"
|
||||
image.thumb(size).encode("jpg", "-quality 90")
|
||||
else
|
||||
image.thumb(size)
|
||||
end
|
||||
image.variant(
|
||||
combine_options: {
|
||||
gravity: "center",
|
||||
resize: "#{size}x#{size}^",
|
||||
extent: "#{size}x#{size}",
|
||||
},
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@ -4,7 +4,7 @@ class TagImageJob < ApplicationJob
|
||||
def perform(image_id:)
|
||||
image = Image.find(image_id)
|
||||
|
||||
tags = Clarifai.new(image.image.file.path).tags
|
||||
tags = Clarifai.new(image.image.download).tags
|
||||
image.update_attributes(tags: tags)
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
class Image < ActiveRecord::Base
|
||||
dragonfly_accessor :image
|
||||
has_one_attached :image
|
||||
|
||||
validates :image, presence: true
|
||||
validates :image, attached: true
|
||||
|
||||
belongs_to :user
|
||||
end
|
||||
|
||||
@ -1,6 +1,4 @@
|
||||
class User < ActiveRecord::Base
|
||||
dragonfly_accessor :avatar
|
||||
|
||||
has_many :images
|
||||
|
||||
def self.find_or_create_from_auth(auth)
|
||||
@ -13,7 +11,6 @@ class User < ActiveRecord::Base
|
||||
uid: auth.uid,
|
||||
username: auth.info.nickname,
|
||||
name: auth.info.name,
|
||||
avatar_url: auth.info.image,
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
7
app/validators/attached_validator.rb
Normal file
7
app/validators/attached_validator.rb
Normal file
@ -0,0 +1,7 @@
|
||||
class AttachedValidator < ActiveModel::EachValidator
|
||||
def validate_each(record, attribute, value)
|
||||
if !value.attached?
|
||||
record.errors.add(attribute, :attached, options)
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1,17 +1,17 @@
|
||||
<picture>
|
||||
<source srcset="<%= image_thumb(@image.image, "2000x2000").url %>"
|
||||
<source srcset="<%= url_for image_variant(@image.image, resize: "2000x2000") %>"
|
||||
media="(min-width: 960px) and (-webkit-min-device-pixel-ratio: 2),
|
||||
(min-width: 960px) and (min-resolution: 192dpi)">
|
||||
<source srcset="<%= image_thumb(@image.image, "1000x1000").url %>"
|
||||
<source srcset="<%= url_for image_variant(@image.image, resize: "1000x1000") %>"
|
||||
media="(min-width: 960px)">
|
||||
<source srcset="<%= image_thumb(@image.image, "1400x1400").url %>"
|
||||
<source srcset="<%= url_for image_variant(@image.image, resize: "1400x1400") %>"
|
||||
media="(min-width: 640px) and (-webkit-min-device-pixel-ratio: 2),
|
||||
(min-width: 640px) and (min-resolution: 192dpi)">
|
||||
<source srcset="<%= image_thumb(@image.image, "700x700").url %>"
|
||||
<source srcset="<%= url_for image_variant(@image.image, resize: "700x700") %>"
|
||||
media="(min-width: 640px)">
|
||||
<source srcset="<%= image_thumb(@image.image, "960x960").url %>"
|
||||
<source srcset="<%= url_for image_variant(@image.image, resize: "960x960") %>"
|
||||
media="(-webkit-min-device-pixel-ratio: 2),
|
||||
(min-resolution: 192dpi)">
|
||||
<source srcset="<%= image_thumb(@image.image, "480x480").url %>">
|
||||
<img src="<%= image_thumb(@image.image, "480x480").url %>" itemprop="image">
|
||||
<source srcset="<%= url_for image_variant(@image.image, resize: "480x480") %>">
|
||||
<img src="<%= url_for image_variant(@image.image, resize: "480x480") %>" itemprop="image">
|
||||
</picture>
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<ul class="images">
|
||||
<% @images.each do |image| %>
|
||||
<li>
|
||||
<%= link_to image_tag(image.image.thumb('200x200#').url),
|
||||
<%= link_to image_tag(image_thumb(image.image, 200)),
|
||||
user_image_path(image), class: :image %><br>
|
||||
<%= link_to 'Delete', user_image_path(image),
|
||||
method: :delete, data: { confirm: 'Are you sure?' } %>
|
||||
|
||||
@ -16,6 +16,9 @@ MyImages::Application.configure do
|
||||
# Don't care if the mailer can't send.
|
||||
config.action_mailer.raise_delivery_errors = false
|
||||
|
||||
# Store files locally.
|
||||
config.active_storage.service = :local
|
||||
|
||||
# Print deprecation notices to the Rails logger.
|
||||
config.active_support.deprecation = :log
|
||||
|
||||
|
||||
@ -72,6 +72,9 @@ MyImages::Application.configure do
|
||||
# Send deprecation notices to registered listeners.
|
||||
config.active_support.deprecation = :notify
|
||||
|
||||
# Store files locally.
|
||||
config.active_storage.service = :local
|
||||
|
||||
# Disable automatic flushing of the log to improve performance.
|
||||
# config.autoflush_log = false
|
||||
|
||||
|
||||
@ -33,4 +33,7 @@ MyImages::Application.configure do
|
||||
|
||||
# Print deprecation notices to the stderr.
|
||||
config.active_support.deprecation = :stderr
|
||||
|
||||
# Store files locally.
|
||||
config.active_storage.service = :test
|
||||
end
|
||||
|
||||
@ -1,27 +0,0 @@
|
||||
require "dragonfly"
|
||||
|
||||
# Configure
|
||||
Dragonfly.app.configure do
|
||||
plugin :imagemagick
|
||||
|
||||
verify_urls true
|
||||
secret "0da375f9754c75707f2a3b4e0c75dffb7ec7d6bb1a4a77e8a8dcfba9037bebce"
|
||||
|
||||
url_format "/media/:job/:name"
|
||||
|
||||
datastore :file,
|
||||
root_path: Rails.root.join("public/system/dragonfly", Rails.env),
|
||||
server_root: Rails.root.join("public")
|
||||
end
|
||||
|
||||
# Logger
|
||||
Dragonfly.logger = Rails.logger
|
||||
|
||||
# Mount as middleware
|
||||
Rails.application.middleware.use Dragonfly::Middleware
|
||||
|
||||
# Add model functionality
|
||||
if defined?(ActiveRecord::Base)
|
||||
ActiveRecord::Base.extend Dragonfly::Model
|
||||
ActiveRecord::Base.extend Dragonfly::Model::Validations
|
||||
end
|
||||
@ -21,3 +21,8 @@
|
||||
|
||||
en:
|
||||
hello: "Hello world"
|
||||
|
||||
activerecord:
|
||||
errors:
|
||||
messages:
|
||||
attached: is not attached
|
||||
|
||||
7
config/storage.yml
Normal file
7
config/storage.yml
Normal file
@ -0,0 +1,7 @@
|
||||
local:
|
||||
service: Disk
|
||||
root: <%= Rails.root.join("storage") %>
|
||||
|
||||
test:
|
||||
service: Disk
|
||||
root: <%= Rails.root.join("tmp/storage") %>
|
||||
@ -0,0 +1,26 @@
|
||||
# This migration comes from active_storage (originally 20170806125915)
|
||||
class CreateActiveStorageTables < ActiveRecord::Migration[5.2]
|
||||
def change
|
||||
create_table :active_storage_blobs do |t|
|
||||
t.string :key, null: false
|
||||
t.string :filename, null: false
|
||||
t.string :content_type
|
||||
t.text :metadata
|
||||
t.bigint :byte_size, null: false
|
||||
t.string :checksum, null: false
|
||||
t.datetime :created_at, null: false
|
||||
|
||||
t.index [ :key ], unique: true
|
||||
end
|
||||
|
||||
create_table :active_storage_attachments do |t|
|
||||
t.string :name, null: false
|
||||
t.references :record, null: false, polymorphic: true, index: false
|
||||
t.references :blob, null: false
|
||||
|
||||
t.datetime :created_at, null: false
|
||||
|
||||
t.index [ :record_type, :record_id, :name, :blob_id ], name: "index_active_storage_attachments_uniqueness", unique: true
|
||||
end
|
||||
end
|
||||
end
|
||||
6
db/migrate/20181019210305_rename_dragonfly_columns.rb
Normal file
6
db/migrate/20181019210305_rename_dragonfly_columns.rb
Normal file
@ -0,0 +1,6 @@
|
||||
class RenameDragonflyColumns < ActiveRecord::Migration[5.2]
|
||||
def change
|
||||
rename_column :images, :image_uid, :df_image_uid
|
||||
rename_column :images, :image_name, :df_image_name
|
||||
end
|
||||
end
|
||||
27
db/schema.rb
27
db/schema.rb
@ -10,11 +10,32 @@
|
||||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 2018_08_26_163510) do
|
||||
ActiveRecord::Schema.define(version: 2018_10_19_210305) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "plpgsql"
|
||||
|
||||
create_table "active_storage_attachments", force: :cascade do |t|
|
||||
t.string "name", null: false
|
||||
t.string "record_type", null: false
|
||||
t.bigint "record_id", null: false
|
||||
t.bigint "blob_id", null: false
|
||||
t.datetime "created_at", null: false
|
||||
t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id"
|
||||
t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true
|
||||
end
|
||||
|
||||
create_table "active_storage_blobs", force: :cascade do |t|
|
||||
t.string "key", null: false
|
||||
t.string "filename", null: false
|
||||
t.string "content_type"
|
||||
t.text "metadata"
|
||||
t.bigint "byte_size", null: false
|
||||
t.string "checksum", null: false
|
||||
t.datetime "created_at", null: false
|
||||
t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true
|
||||
end
|
||||
|
||||
create_table "delayed_jobs", force: :cascade do |t|
|
||||
t.integer "priority", default: 0, null: false
|
||||
t.integer "attempts", default: 0, null: false
|
||||
@ -32,8 +53,8 @@ ActiveRecord::Schema.define(version: 2018_08_26_163510) do
|
||||
|
||||
create_table "images", force: :cascade do |t|
|
||||
t.integer "user_id"
|
||||
t.string "image_uid"
|
||||
t.string "image_name"
|
||||
t.string "df_image_uid"
|
||||
t.string "df_image_name"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.string "tags", default: [], array: true
|
||||
|
||||
@ -6,8 +6,8 @@ class Clarifai
|
||||
API_URL = "https://api.clarifai.com/v2/models/" \
|
||||
"aaa03c23b3724a16a56b629203edc62c/outputs".freeze
|
||||
|
||||
def initialize(image_path)
|
||||
@image_path = image_path
|
||||
def initialize(image_data)
|
||||
@image_data = image_data
|
||||
end
|
||||
|
||||
def tags
|
||||
@ -16,6 +16,8 @@ class Clarifai
|
||||
|
||||
private
|
||||
|
||||
attr_reader :image_data
|
||||
|
||||
def extract_tags(response_hash)
|
||||
response_hash["outputs"][0]["data"]["concepts"].map do |concept|
|
||||
concept["name"]
|
||||
@ -40,10 +42,6 @@ class Clarifai
|
||||
end
|
||||
|
||||
def image_base64
|
||||
Base64.encode64(image_file)
|
||||
end
|
||||
|
||||
def image_file
|
||||
File.read(@image_path)
|
||||
Base64.encode64(image_data)
|
||||
end
|
||||
end
|
||||
|
||||
11
lib/tasks/images_migrate.rake
Normal file
11
lib/tasks/images_migrate.rake
Normal file
@ -0,0 +1,11 @@
|
||||
namespace :images do
|
||||
desc "Migrate all the images to Active Storage"
|
||||
task migrate: :environment do
|
||||
Image.all.each do |image|
|
||||
image.image.attach(
|
||||
io: image.df_image.file,
|
||||
filename: image.df_image.name,
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -15,10 +15,14 @@ feature "user manages images" do
|
||||
|
||||
it "deletes the image" do
|
||||
user = User.create(uid: "1")
|
||||
Image.create(
|
||||
image = Image.new(
|
||||
user: user,
|
||||
image: File.new("#{Rails.root}/spec/fixtures/spectrum.jpg"),
|
||||
)
|
||||
image.image.attach(
|
||||
io: File.open("#{Rails.root}/spec/fixtures/spectrum.jpg"),
|
||||
filename: "spectrum.jpg",
|
||||
)
|
||||
image.save
|
||||
|
||||
sign_in(user)
|
||||
click_on("Delete")
|
||||
|
||||
@ -4,11 +4,15 @@ feature "user manages image tags by visitng images show page" do
|
||||
scenario "and can see an X to link for deletion on a tag" do
|
||||
tags = ["one", "two"]
|
||||
user = User.create(uid: "123")
|
||||
Image.create(
|
||||
image = Image.new(
|
||||
user: user,
|
||||
tags: tags,
|
||||
image: File.new("#{Rails.root}/spec/fixtures/spectrum.jpg"),
|
||||
)
|
||||
image.image.attach(
|
||||
io: File.open("#{Rails.root}/spec/fixtures/spectrum.jpg"),
|
||||
filename: "spectrum.jpg",
|
||||
)
|
||||
image.save
|
||||
|
||||
sign_in(user)
|
||||
page.find(".image").click
|
||||
@ -21,11 +25,15 @@ feature "user manages image tags by visitng images show page" do
|
||||
scenario "and can click the link to delete a tag", js: true do
|
||||
tags = ["one", "two"]
|
||||
user = User.create(uid: "123")
|
||||
Image.create(
|
||||
image = Image.new(
|
||||
user: user,
|
||||
tags: tags,
|
||||
image: File.new("#{Rails.root}/spec/fixtures/spectrum.jpg"),
|
||||
)
|
||||
image.image.attach(
|
||||
io: File.open("#{Rails.root}/spec/fixtures/spectrum.jpg"),
|
||||
filename: "spectrum.jpg",
|
||||
)
|
||||
image.save
|
||||
|
||||
sign_in(user)
|
||||
page.find(".image").click
|
||||
@ -41,11 +49,15 @@ feature "user manages image tags by visitng images show page" do
|
||||
tags = ["one", "two"]
|
||||
user = User.create(uid: "123")
|
||||
new_tag = "newtag"
|
||||
Image.create(
|
||||
image = Image.new(
|
||||
user: user,
|
||||
tags: tags,
|
||||
image: File.new("#{Rails.root}/spec/fixtures/spectrum.jpg"),
|
||||
)
|
||||
image.image.attach(
|
||||
io: File.open("#{Rails.root}/spec/fixtures/spectrum.jpg"),
|
||||
filename: "spectrum.jpg",
|
||||
)
|
||||
image.save
|
||||
|
||||
sign_in(user)
|
||||
page.find(".image").click
|
||||
|
||||
@ -4,9 +4,12 @@ describe TagImageJob, type: :job do
|
||||
describe ".perform" do
|
||||
it "tags an image" do
|
||||
stub_clarifai(%w[computers technology])
|
||||
image = Image.create(
|
||||
image: "#{Rails.root}/spec/fixtures/spectrum.jpg",
|
||||
image = Image.new
|
||||
image.image.attach(
|
||||
io: File.open("#{Rails.root}/spec/fixtures/spectrum.jpg"),
|
||||
filename: "spectrum.jpg",
|
||||
)
|
||||
image.save
|
||||
|
||||
TagImageJob.perform_now(image_id: image.id)
|
||||
image.reload
|
||||
|
||||
@ -11,7 +11,7 @@ describe Clarifai do
|
||||
it "predicts tags for our image" do
|
||||
stub_api(%w[computer technology])
|
||||
|
||||
clarifai = Clarifai.new("spec/fixtures/spectrum.jpg")
|
||||
clarifai = Clarifai.new(File.read("spec/fixtures/spectrum.jpg"))
|
||||
|
||||
expect(clarifai.tags).to eq(%w[computer technology])
|
||||
end
|
||||
|
||||
@ -1,11 +1,7 @@
|
||||
require "rails_helper"
|
||||
|
||||
describe Image do
|
||||
it { is_expected.to validate_presence_of(:image) }
|
||||
it { is_expected.to belong_to(:user) }
|
||||
|
||||
it "should not be valid without an image" do
|
||||
image = Image.new
|
||||
expect(image).not_to be_valid
|
||||
end
|
||||
it { is_expected.to have_attached_file(:image) }
|
||||
it { is_expected.to validate_attachment_of(:image) }
|
||||
end
|
||||
|
||||
7
spec/support/matchers/have_attached_file.rb
Normal file
7
spec/support/matchers/have_attached_file.rb
Normal file
@ -0,0 +1,7 @@
|
||||
RSpec::Matchers.define :have_attached_file do |name|
|
||||
match do |record|
|
||||
file = record.send(name)
|
||||
|
||||
file.respond_to?(:attach)
|
||||
end
|
||||
end
|
||||
45
spec/support/matchers/validate_attachment_of.rb
Normal file
45
spec/support/matchers/validate_attachment_of.rb
Normal file
@ -0,0 +1,45 @@
|
||||
require "rspec/expectations"
|
||||
|
||||
RSpec::Matchers.define :validate_attachment_of do |attr_name|
|
||||
match do |record|
|
||||
matcher.matches?(record, attr_name)
|
||||
end
|
||||
|
||||
chain :on do |validation_context|
|
||||
matcher.on(validation_context)
|
||||
end
|
||||
|
||||
chain :with_message do |message|
|
||||
matcher.with_message(message)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def matcher
|
||||
@matcher ||= ValidateAttachmentOfMatcher.new
|
||||
end
|
||||
|
||||
class ValidateAttachmentOfMatcher
|
||||
def on(validation_context)
|
||||
@validation_context = validation_context
|
||||
end
|
||||
|
||||
def with_message(message)
|
||||
@message = message
|
||||
end
|
||||
|
||||
def matches?(record, attr_name)
|
||||
record.send(attr_name).purge
|
||||
record.valid?(validation_context)
|
||||
record.errors[attr_name].include? message
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :validation_context
|
||||
|
||||
def message
|
||||
@message || I18n.translate("activerecord.errors.messages.attached")
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Reference in New Issue
Block a user