Skip to content

Commit

Permalink
Init commit
Browse files Browse the repository at this point in the history
  • Loading branch information
jsvisa committed Nov 13, 2014
0 parents commit 1631c90
Show file tree
Hide file tree
Showing 13 changed files with 452 additions and 0 deletions.
14 changes: 14 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/.bundle/
/.yardoc
/Gemfile.lock
/_yardoc/
/coverage/
/doc/
/pkg/
/spec/reports/
/tmp/
*.bundle
*.so
*.o
*.a
mkmf.log
3 changes: 3 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
source 'https://ruby.taobao.org'

gemspec
22 changes: 22 additions & 0 deletions LICENSE.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
Copyright (c) 2014 jsvisa

MIT License

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.
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Upyun

TODO: Write a gem description

## Installation

Add this line to your application's Gemfile:

```ruby
gem 'upyun'
```

And then execute:

$ bundle

Or install it yourself as:

$ gem install upyun

## Usage

TODO: Write usage instructions here

## Contributing

1. Fork it ( https://github.com/[my-github-username]/upyun/fork )
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
5. Create a new Pull Request
2 changes: 2 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
require "bundler/gem_tasks"

9 changes: 9 additions & 0 deletions lib/upyun.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
require 'upyun/version'
require 'upyun/rest'
require 'upyun/form'

module UpYun
DOMAIN = 'api.upyun.com'
ED_AUTO = "v0.#{DOMAIN}"
ED_LIST = (0..3).map { |e| "v#{e}.#{DOMAIN}" }
end
78 changes: 78 additions & 0 deletions lib/upyun/form.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# encoding: utf-8
require 'restclient'
require 'digest/md5'
require 'base64'
require 'json'

module UpYun
class Form
VALID_PARAMS = %i(
bucket
save-key
expiration
allow-file-type
content-length-range
content-md5
content-secret
content-type
image-width-range
image-height-range
notify-url
return-url
x-gmkerl-thumbnail
x-gmkerl-type
x-gmkerl-value
x-gmkerl-quality
x-gmkerl-unsharp
x-gmkerl-rotate
x-gmkerl-crop
x-gmkerl-exif-switch
ext-param
)

attr_accessor :endpoint, :bucket, :password

def initialize(password, opts)
@password = password
@endpoint = ED_AUTO
raise ArgumentError, "bucket must be assigned" unless opts.key?(:bucket)
@bucket = opts[:bucket]
@opts = opts.dup
@opts[:"save-key"] = opts[:"save-key"] || opts[:save_key] || '/{year}/{mon}/{day}/{filename}{.suffix}'
@opts[:expiration] = opts[:expiration] || Time.now.to_i + 600
end

def endpoint=(ep)
raise ArgumentError, "Valid endpoint are #{UpYun::ED_LIST}" unless UpYun::ED_LIST.member?(ep)
@endpoint = ep
end

def upload(file, opts={})
opts = @opts.merge(opts)
payload = {policy: policy(opts), signature: signature, file: File.new(file, "rb")}
RestClient.post("http://#{@endpoint}/#{@bucket}", payload) do |res|
body = JSON.parse(res.body, symbolize_names: true)

# UpYun have a small bug for the code, we have to adjust it to integer
body[:code] = body[:code].to_i
body
end
end

private
def policy(opts)
@_policy = Base64.encode64(policy_json(opts))
end

def signature
Digest::MD5.hexdigest("#{@_policy}&#{@password}")
end

def policy_json(opts)
policies = VALID_PARAMS.reduce({}) do |memo, e|
(v = opts[e]) ? memo.merge!({e => v}) : memo
end
policies.to_json
end
end
end
132 changes: 132 additions & 0 deletions lib/upyun/rest.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
# encoding: utf-8
require 'restclient'
require 'digest/md5'
require 'uri'

module UpYun
class Rest
attr_accessor :endpoint

def initialize(bucket, username, password, endpoint=nil)
@bucket = bucket
@username = username
@password = md5(password)
@endpoint = endpoint || UpYun::ED_AUTO
end

def endpoint=(ep)
raise ArgumentError, "Valid endpoint are #{UpYun::ED_LIST}" unless UpYun::ED_LIST.member?(ep)
@endpoint = ep
end

def put(path, file, headers={})
raise ArgumentError, "'file' is not an instance of String" unless file.is_a?(String)
headers = headers.merge({"mkdir" => true}) unless headers.key?("mkdir")
options = if File.file?(file)
{body: File.read(file), length: File.size(file), headers: headers}
else
{body: file, length: file.length, headers: headers}
end

request(:put, path, options)
end

def get(path, savepath=nil)
res = request(:get, path)
return res if res.is_a?(Hash)

savepath ? File.write(savepath, res) : res
end

def delete(path)
request(:delete, path)
end

def mkdir(path, auto=true)
request(:post, path, {headers: {folder: true, mkdir: auto}})
end

def getlist(path="/")
res = request(:get, path)
return res if res.is_a?(Hash)
res.split("\n").map do |f|
attrs = f.split("\t")
{
name: attrs[0],
type: attrs[1] == "N" ? :file : :folder,
length: attrs[2].to_i,
last_modified: attrs[3].to_i
}
end
end

def usage(path="/")
res = request(:get, path, {params: "usage"})
return res if res.is_a?(Hash)

# RestClient has a bug, body.to_i returns the code instead of body,
# see more on https://github.com/rest-client/rest-client/pull/103
res.dup.to_i
end

private

def res_msg(res)
return true if res.respond_to?(:code) and res.code == 200
res
end

def fullpath(path)
"/#{@bucket}#{URI.encode(URI.decode(path[0] == '/' ? path : '/' + path))}"
end

def encode(fullpath, params)
URI.join("http://#{@endpoint}", fullpath, params.nil? ? '' : '?' + params).to_s
end

def request(method, path, options={})
fullpath = fullpath(path)
url = encode(fullpath, options[:params])
headers = options[:headers] || {}
date = gmdate
length = options[:length] || 0
headers.merge!({
'Date' => date,
'Authorization' => sign(method, date, fullpath, length)
})

if [:post, :patch, :put].include? method
RestClient.send(method, url, options[:body].nil? ? "" : options[:body], headers) do |res|
case res.code
when 200
true
else
{error: {code: res.code, msg: res.body}}
end
end
else
RestClient.send(method, url, headers) do |res|
case res.code
when 200
method == :get ? res.body : true
else
{error: {code: res.code, msg: res.body}}
end
end
end
end

def gmdate
Time.now.utc.strftime('%a, %d %b %Y %H:%M:%S GMT')
end

def sign(method, date, path, length)
sign = "#{method.to_s.upcase}&#{path}&#{date}&#{length}&#{@password}"
"UpYun #{@username}:#{md5(sign)}"
end

def md5(str)
Digest::MD5.hexdigest(str)
end
end
end
3 changes: 3 additions & 0 deletions lib/upyun/version.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module UpYun
VERSION = "0.0.1"
end
8 changes: 8 additions & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
require 'rubygems'
require 'rspec'
require 'bundler/setup'

$LOAD_PATH.unshift(File.dirname(__FILE__))
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))

require 'upyun'
Binary file added spec/upyun.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 1631c90

Please sign in to comment.