Skip to content

Commit

Permalink
Added documentation for all classes [ci skip]
Browse files Browse the repository at this point in the history
Closes #8
  • Loading branch information
elektronaut committed Jun 23, 2014
1 parent 1a29097 commit 0056158
Show file tree
Hide file tree
Showing 10 changed files with 277 additions and 5 deletions.
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# Shrouded [![Build Status](https://travis-ci.org/elektronaut/shrouded.png)](https://travis-ci.org/elektronaut/shrouded) [![Code Climate](https://codeclimate.com/github/elektronaut/shrouded.png)](https://codeclimate.com/github/elektronaut/shrouded) [![Code Climate](https://codeclimate.com/github/elektronaut/shrouded/coverage.png)](https://codeclimate.com/github/elektronaut/shrouded)

**Warning:** Work in progress, the API is subject to change.

Shrouded handles file uploads for your Rails app.
It's similar to [Paperclip](https://github.com/thoughtbot/paperclip)
and [Carrierwave](https://github.com/carrierwaveuploader/carrierwave),
Expand All @@ -19,12 +17,14 @@ a file with changed content is by definition a different file.

It does not do any processing. The idea is to provide a simple foundation
other gems can build on. If you are looking to handle uploaded images,
the next version of
[DynamicImage](https://github.com/elektronaut/dynamic_image)
will be built on top of Shrouded.
check out [DynamicImage](https://github.com/elektronaut/dynamic_image).

Requires Rails 4.1+ and Ruby 1.9.3+.

## Documentation

[Documentation on RubyDoc.info](http://rdoc.info/github/elektronaut/shrouded)

## Installation

Add the gem to your Gemfile and run `bundle install`:
Expand Down
5 changes: 5 additions & 0 deletions lib/shrouded/jobs/delete.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

module Shrouded
module Jobs
# = Shrouded Delete Job
#
# Handles delayed deletion of objects.
#
# Shrouded::Jobs::Delete.enqueue("documents", hash)
class Delete < ActiveJob::Base
queue_as :shrouded

Expand Down
5 changes: 5 additions & 0 deletions lib/shrouded/jobs/store.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

module Shrouded
module Jobs
# = Shrouded Store Job
#
# Handles delayed storage of objects.
#
# Shrouded::Jobs::Store.enqueue("documents", hash)
class Store < ActiveJob::Base
queue_as :shrouded

Expand Down
71 changes: 71 additions & 0 deletions lib/shrouded/layer.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,49 @@
# encoding: utf-8

module Shrouded
# = Shrouded Layer
#
# Represents a layer of storage. It's a wrapper around
# <tt>Fog::Storage</tt>, any provider supported by Fog should be usable.
#
# ==== Options
#
# * <tt>:delayed</tt> - Delayed layers will be processed outside of
# the request cycle by ActiveJob.
# * <tt>:readonly</tt> - Readonly layers can only be read from,
# not written to.
# * <tt>:public</tt> - Objects stored in public layers will have the
# public readable flag set if supported by the storage provider.
# * <tt>:path</tt> - Directory name to use for the store. For Amazon S3,
# this will be the name of the bucket.
#
# ==== Examples
#
# This creates a local storage layer. It's a good idea to have a local layer
# first, this provides you with a cache on disk that will be faster than
# reading from the cloud.
#
# Shrouded::Layer.new(
# Fog::Storage.new({
# provider: 'Local',
# local_root: Rails.root.join('db', 'shrouded')
# }),
# path: Rails.env
# )
#
# This creates a delayed layer on Amazon S3. ActiveJob will kick in and
# and transfer content from one of the immediate layers later at it's
# leisure.
#
# Shrouded::Layer.new(
# Fog::Storage.new({
# provider: 'AWS',
# aws_access_key_id: YOUR_AWS_ACCESS_KEY_ID,
# aws_secret_access_key: YOUR_AWS_SECRET_ACCESS_KEY
# }),
# path: "my_bucket",
# delayed: true
# )
class Layer
attr_reader :connection

Expand All @@ -13,42 +56,70 @@ def initialize(connection, options={})
@path = options[:path]
end

# Returns true if the layer is a delayed layer.
def delayed?
@delayed
end

# Returns true if the layer isn't a delayed layer.
def immediate?
!delayed?
end

# Returns true if the layer isn't a delayed layer.
def public?
@public
end

# Returns true if the layer is read only.
def readonly?
@readonly
end

# Returns true if the layer is writeable.
def writeable?
!readonly?
end

# Stores a file.
#
# hash = Digest::SHA1.file(file.path).hexdigest
# layer.store("documents", hash, path)
#
# Hash must be a hex digest of the file content. If an object with the
# supplied hash already exists, no action will be performed. In other
# words, no data will be overwritten if a hash collision occurs.
#
# Returns an instance of Fog::Model, or raises an error if the layer
# is readonly.
def store(type, hash, file)
raise Shrouded::Errors::ReadOnlyError if readonly?
store!(type, hash, file)
end

# Returns true if a object with the given hash exists.
#
# layer.exists?("documents", hash)
def exists?(type, hash)
(directory(type, hash) &&
directory(type, hash).files.head(key_component(type, hash))) ? true : false
end

# Retrieves a file from the store.
#
# layer.get("documents", hash)
def get(type, hash)
if dir = directory(type, hash)
dir.files.get(key_component(type, hash))
end
end

# Deletes a file from the store.
#
# layer.delete("documents", hash)
#
# Returns true if the file was deleted, or false if it could not be found.
# Raises an error if the layer is readonly.
def delete(type, hash)
raise Shrouded::Errors::ReadOnlyError if readonly?
delete!(type, hash)
Expand Down
14 changes: 14 additions & 0 deletions lib/shrouded/layers.rb
Original file line number Diff line number Diff line change
@@ -1,53 +1,67 @@
# encoding: utf-8

module Shrouded
# = Shrouded Layers
#
# Represents a collection of layers.
class Layers
include Enumerable

def initialize(layers=[])
@layers = layers
end

# Adds a layer to the collection.
def <<(layer)
@layers << layer
end

# Clears all layers from the collection.
def clear!
@layers = []
end

# Iterates over the layers.
def each
@layers.each { |layer| yield layer }
end

# Returns a new instance containing only the delayed layers.
def delayed
self.class.new select { |layer| layer.delayed? }
end

# Returns true if one or more delayed layers exist.
def delayed?
delayed.any?
end

# Returns a new instance containing only the immediate layers.
def immediate
self.class.new select { |layer| layer.immediate? }
end

# Returns true if one or more immediate layers exist.
def immediate?
immediate.any?
end

# Returns a new instance containing only the readonly layers.
def readonly
self.class.new select { |layer| layer.readonly? }
end

# Returns true if one or more readonly layers exist.
def readonly?
readonly.any?
end

# Returns a new instance containing only the writeable layers.
def writeable
self.class.new select { |layer| layer.writeable? }
end

# Returns true if one or more writeable layers exist.
def writeable?
writeable.any?
end
Expand Down
83 changes: 83 additions & 0 deletions lib/shrouded/model.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,81 @@
require 'shrouded/model/data'

module Shrouded
# = Shrouded Model
#
# ActiveModel extension for the model holding your data. To use it,
# simply include the module in your model:
#
# class Document < ActiveRecord::Base
# include Shrouded::Model
# end
#
# You'll need to define a few attributes in your database table.
# Here's a minimal migration:
#
# create_table :documents do |t|
# t.string :content_hash
# t.string :content_type
# t.integer :content_length
# t.string :filename
# end
#
# You can override the names of any of these by setting
# <tt>shrouded_attributes</tt>.
#
# class Document < ActiveRecord::Base
# include Shrouded::Model
# self.shrouded_attributes = {
# filename: :my_filename,
# content_length: :filesize
# }
# end
#
# == Usage
#
# To save a file, simply assign to the <tt>file</tt> attribute.
#
# document = Document.create(file: params.permit(:file))
#
# <tt>content_type</tt> and <tt>filename</tt> will automatically be set if
# the supplied object quacks like a file. <tt>content_length</tt> will always
# be set. <tt>content_hash</tt> won't be set until the record is being saved.
#
# If you don't care about filenames and content types and just want to store
# a binary blob, you can also just set the <tt>data</tt> attribute.
#
# my_data = File.read('document.pdf')
# document.update(data: my_data)
#
# The data won't be stored until the record is saved, and not unless
# the record is valid.
#
# To retrieve your data, simply read the <tt>data</tt> attribute. The file
# will be lazily loaded from the store on demand and cached in memory as long
# as the record stays in scope.
#
# my_data = document.data
#
# Destroying a record will delete the file from the store, unless another
# record also refers to the same hash. Similarly, stale files will be purged
# when content changes.
#
# == Validations
#
# No validation is performed by default. If you want to ensure that data is
# present, use the <tt>validates_data_presence</tt> method.
#
# class Document < ActiveRecord::Base
# include Shrouded::Model
# validates_data_presence
# end
#
# If you want to validate content types, size or similar, simply use standard
# Rails validations on the metadata attributes:
#
# validates :content_type, presence: true, format: /\Aapplication\/(x\-)?pdf\z/
# validates :filename, presence: true, format: /\A[\w_\-\.]+\.pdf\z/i
# validates :content_length, numericality: { less_than: 5.megabytes }
module Model
extend ActiveSupport::Concern

Expand All @@ -13,14 +88,18 @@ module Model
after_destroy :delete_data
end

# Returns the data as a binary string, or nil if no data has been set.
def data
shrouded_data.read
end

# Returns true if data is set.
def data?
shrouded_data.any?
end

# Assigns new data. This also sets <tt>content_length</tt>, and resets
# <tt>content_hash</tt> to nil.
def data=(new_data)
new_data = Shrouded::Model::Data.new(self, new_data)
attribute_will_change!('data') unless new_data == shrouded_data
Expand All @@ -29,10 +108,14 @@ def data=(new_data)
shrouded_set :content_length, shrouded_data.content_length
end

# Returns true if the data has been changed since the object was last saved.
def data_changed?
changes.include?('data')
end

# Assigns new data from an uploaded file. In addition to the actions
# performed by <tt>data=</tt>, this will set <tt>content_type</tt> and
# <tt>filename</tt>.
def file=(file)
self.data = file
shrouded_set :content_type, file.content_type
Expand Down
Loading

0 comments on commit 0056158

Please sign in to comment.