aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Heinemeier Hansson <david@loudthinking.com>2017-06-30 19:12:58 +0200
committerDavid Heinemeier Hansson <david@loudthinking.com>2017-06-30 19:12:58 +0200
commitdd50144bcd4dbd605995123ab5afc99e40e9a630 (patch)
tree81698d44cf2f9dd62e24a95f945f6904689be2f2
downloadrails-dd50144bcd4dbd605995123ab5afc99e40e9a630.tar.gz
rails-dd50144bcd4dbd605995123ab5afc99e40e9a630.tar.bz2
rails-dd50144bcd4dbd605995123ab5afc99e40e9a630.zip
First sketching
-rw-r--r--Gemfile6
-rw-r--r--Gemfile.lock40
-rw-r--r--MIT-LICENSE20
-rw-r--r--README.md45
-rw-r--r--Rakefile10
-rw-r--r--activefile.gemspec20
-rw-r--r--lib/active_file.rb8
-rw-r--r--lib/active_file/blob.rb45
-rw-r--r--lib/active_file/filename.rb31
-rw-r--r--lib/active_file/migration.rb14
-rw-r--r--lib/active_file/purge_job.rb7
-rw-r--r--lib/active_file/railtie.rb6
-rw-r--r--lib/active_file/store.rb26
-rw-r--r--test/blob_test.rb7
-rw-r--r--test/test_helper.rb4
15 files changed, 289 insertions, 0 deletions
diff --git a/Gemfile b/Gemfile
new file mode 100644
index 0000000000..dbddb9f913
--- /dev/null
+++ b/Gemfile
@@ -0,0 +1,6 @@
+source 'https://rubygems.org'
+
+gemspec
+
+gem 'rake'
+gem 'byebug'
diff --git a/Gemfile.lock b/Gemfile.lock
new file mode 100644
index 0000000000..0d106cecfc
--- /dev/null
+++ b/Gemfile.lock
@@ -0,0 +1,40 @@
+PATH
+ remote: .
+ specs:
+ google_sign_in (0.1)
+ activesupport (>= 5.1)
+ google-id-token (>= 1.3.1)
+
+GEM
+ remote: https://rubygems.org/
+ specs:
+ activesupport (5.1.1)
+ concurrent-ruby (~> 1.0, >= 1.0.2)
+ i18n (~> 0.7)
+ minitest (~> 5.1)
+ tzinfo (~> 1.1)
+ byebug (9.0.6)
+ concurrent-ruby (1.0.5)
+ google-id-token (1.3.1)
+ jwt
+ multi_json
+ i18n (0.8.1)
+ jwt (1.5.6)
+ minitest (5.10.2)
+ multi_json (1.12.1)
+ rake (12.0.0)
+ thread_safe (0.3.6)
+ tzinfo (1.2.3)
+ thread_safe (~> 0.1)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ bundler (~> 1.15)
+ byebug
+ google_sign_in!
+ rake
+
+BUNDLED WITH
+ 1.15.0
diff --git a/MIT-LICENSE b/MIT-LICENSE
new file mode 100644
index 0000000000..4e1c6cad79
--- /dev/null
+++ b/MIT-LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2017 David Heinemeier Hansson, Basecamp
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000000..fccaa2d2bb
--- /dev/null
+++ b/README.md
@@ -0,0 +1,45 @@
+# Active File
+
+...
+
+## Example
+
+class Person < ApplicationRecord
+ has_one :avatar
+end
+
+class Avatar < ApplicationRecord
+ belongs_to :person
+ belongs_to :image, class_name: 'ActiveFile::Blob'
+
+ has_file :image
+end
+
+avatar.image.url(expires_in: 5.minutes)
+
+
+class ActiveFile::DownloadsController < ActionController::Base
+ def show
+ head :ok, ActiveFile::Blob.locate(params[:id]).download_headers
+ end
+end
+
+
+class AvatarsController < ApplicationController
+ def create
+ # @avatar = Avatar.create \
+ # image: ActiveFile::Blob.save!(file_name: params.require(:name), content_type: request.content_type, data: request.body)
+ @avatar = Avatar.create! image: Avatar.image.extract_from(request)
+ end
+end
+
+
+class ProfilesController < ApplicationController
+ def update
+ @person.update! avatar: @person.avatar.update!(image: )
+ end
+end
+
+## License
+
+Google Sign-In for Rails is released under the [MIT License](https://opensource.org/licenses/MIT).
diff --git a/Rakefile b/Rakefile
new file mode 100644
index 0000000000..a61ad18bca
--- /dev/null
+++ b/Rakefile
@@ -0,0 +1,10 @@
+require "bundler/setup"
+require "bundler/gem_tasks"
+require "rake/testtask"
+
+Rake::TestTask.new do |test|
+ test.libs << "test"
+ test.test_files = FileList["test/*_test.rb"]
+end
+
+task default: :test
diff --git a/activefile.gemspec b/activefile.gemspec
new file mode 100644
index 0000000000..20deecff23
--- /dev/null
+++ b/activefile.gemspec
@@ -0,0 +1,20 @@
+Gem::Specification.new do |s|
+ s.name = 'activefile'
+ s.version = '0.1'
+ s.authors = 'David Heinemeier Hansson'
+ s.email = 'david@basecamp.com'
+ s.summary = 'Store files in Rails applications'
+ s.homepage = 'https://github.com/rails/activefile'
+ s.license = 'MIT'
+
+ s.required_ruby_version = '>= 1.9.3'
+
+ s.add_dependency 'activesupport', '>= 5.1'
+ s.add_dependency 'activerecord', '>= 5.1'
+ s.add_dependency 'activejob', '>= 5.1'
+
+ s.add_development_dependency 'bundler', '~> 1.15'
+
+ s.files = `git ls-files`.split("\n")
+ s.test_files = `git ls-files -- test/*`.split("\n")
+end
diff --git a/lib/active_file.rb b/lib/active_file.rb
new file mode 100644
index 0000000000..7dbcf95163
--- /dev/null
+++ b/lib/active_file.rb
@@ -0,0 +1,8 @@
+require "active_record"
+require "active_file/railtie" if defined?(Rails)
+
+module ActiveFile
+ extend ActiveSupport::Autoload
+
+ autoload :Blob
+end \ No newline at end of file
diff --git a/lib/active_file/blob.rb b/lib/active_file/blob.rb
new file mode 100644
index 0000000000..248b136903
--- /dev/null
+++ b/lib/active_file/blob.rb
@@ -0,0 +1,45 @@
+# Schema: id, token, filename, content_type, metadata, byte_size, digest, created_at
+class ActiveFile::Blob < ActiveRecord::Base
+ self.table_name = "rails_active_file_blobs"
+
+ store :metadata, coder: JSON
+ has_secure_token
+
+ class_attribute :verifier, default: -> { Rails.application.message_verifier('ActiveFile') }
+ class_attribute :storage
+
+ class << self
+ def find_verified(signed_id)
+ find(verifier.verify(signed_id))
+ end
+
+ def build_after_upload(data:, filename:, content_type: nil, metadata: nil)
+ new.tap do |blob|
+ blob.filename = name
+ blob.content_type = Marcel::MimeType.for(data, name: name, declared_type: content_type)
+ blob.data = data
+ end
+ end
+
+ def create_after_upload!(data:, filename:, content_type: nil, metadata: nil)
+ build_after_upload(data: data, filename: filename, content_type: content_type, metadata: metadata).tap(&:save!)
+ end
+ end
+
+ def filename
+ Filename.new(filename)
+ end
+
+ def delete
+ storage.delete token
+ end
+
+ def purge
+ delete
+ destroy
+ end
+
+ def purge_later
+ ActiveFile::PurgeJob.perform_later(self)
+ end
+end
diff --git a/lib/active_file/filename.rb b/lib/active_file/filename.rb
new file mode 100644
index 0000000000..b3c184e26c
--- /dev/null
+++ b/lib/active_file/filename.rb
@@ -0,0 +1,31 @@
+class ActiveFile::Filename
+ include Comparable
+
+ def initialize(filename)
+ @filename = filename
+ end
+
+ def extname
+ File.extname(@filename)
+ end
+
+ def extension
+ extname.from(1)
+ end
+
+ def base
+ File.basename(@filename, extname)
+ end
+
+ def sanitized
+ @filename.encode(Encoding::UTF_8, invalid: :replace, undef: :replace, replace: "�").strip.tr("\u{202E}%$|:;/\t\r\n\\", "-")
+ end
+
+ def to_s
+ sanitized.to_s
+ end
+
+ def <=>(other)
+ to_s.downcase <=> other.to_s.downcase
+ end
+end
diff --git a/lib/active_file/migration.rb b/lib/active_file/migration.rb
new file mode 100644
index 0000000000..0f6b0a3fd2
--- /dev/null
+++ b/lib/active_file/migration.rb
@@ -0,0 +1,14 @@
+class ActiveFile::CreateBlobs < ActiveRecord::Migration[5.2]
+ def change
+ create_table :rails_active_file_blobs do |t|
+ t.string :token
+ t.string :filename
+ t.string :content_type
+ t.integer :byte_size
+ t.string :digest
+ t.time :created_at
+
+ t.index [ :token ], unique: true
+ end
+ end
+end
diff --git a/lib/active_file/purge_job.rb b/lib/active_file/purge_job.rb
new file mode 100644
index 0000000000..1a967db2f0
--- /dev/null
+++ b/lib/active_file/purge_job.rb
@@ -0,0 +1,7 @@
+class ActiveFile::PurgeJob < ActiveJob::Base
+ retry_on ActiveFile::StorageException
+
+ def perform(blob)
+ blob.purge
+ end
+end
diff --git a/lib/active_file/railtie.rb b/lib/active_file/railtie.rb
new file mode 100644
index 0000000000..ccba844742
--- /dev/null
+++ b/lib/active_file/railtie.rb
@@ -0,0 +1,6 @@
+require 'rails/railtie'
+
+module ActiveFile
+ class Engine < ::Rails::Engine
+ end
+end
diff --git a/lib/active_file/store.rb b/lib/active_file/store.rb
new file mode 100644
index 0000000000..bdac4eab9e
--- /dev/null
+++ b/lib/active_file/store.rb
@@ -0,0 +1,26 @@
+class ActiveFile::Store
+ def upload(key, data)
+ end
+
+ def download(key)
+ end
+
+ def delete(key)
+ end
+
+ def exists?(key)
+ end
+
+ def url(key)
+ end
+
+ def checksum(key)
+ end
+
+
+ def copy(from_key:, to_key:)
+ end
+
+ def move(from_key:, to_key:)
+ end
+end
diff --git a/test/blob_test.rb b/test/blob_test.rb
new file mode 100644
index 0000000000..3ebde08b90
--- /dev/null
+++ b/test/blob_test.rb
@@ -0,0 +1,7 @@
+require 'test_helper'
+
+class ActiveFile::BlobTest < ActiveSupport::TestCase
+ test "truth" do
+ assert true
+ end
+end
diff --git a/test/test_helper.rb b/test/test_helper.rb
new file mode 100644
index 0000000000..c05ba0c70c
--- /dev/null
+++ b/test/test_helper.rb
@@ -0,0 +1,4 @@
+require 'bundler/setup'
+require 'active_support'
+require 'active_support/testing/autorun'
+require 'byebug'