carrierwave-yandexfotki 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in carrierwave-yandexfotki.gemspec
4
+ gemspec
@@ -0,0 +1,106 @@
1
+ YandexFotki storage for CarrierWave gem
2
+ =======================================
3
+
4
+ This gem provides a simple way to upload image files from Rails application to [fotki.yandex.ru](http://fotki.yandex.ru) service.
5
+
6
+ Getting Started
7
+ ---------------
8
+
9
+ In Rails, add it to your Gemfile:
10
+
11
+ gem 'carrierwave-yandexfotki'
12
+
13
+ Quick Start
14
+ -----------
15
+
16
+ ### Creating uploader
17
+
18
+ Start off by creating an uploader:
19
+
20
+ app/uploaders/image_uploader.rb
21
+
22
+ It should look something like this:
23
+
24
+ class ImageUploader < CarrierWave::Uploader::Base
25
+
26
+ storage CarrierWave::Storage::YandexFotki
27
+
28
+ yandex_login 'login'
29
+ yandex_password 'password'
30
+
31
+ ##
32
+ # Uncomment this if you want use 'net/http' library instead 'curb'.
33
+ # Also you need install 'multipart-post' gem for use this option.
34
+ #
35
+ #yandex_net_http true
36
+
37
+ ##
38
+ # This required for removing old image from fotki.yandex.ru
39
+ # on updating image attached to model
40
+ #
41
+ before :cache, :remove_old_file_before_cache
42
+
43
+ def remove_old_file_before_cache(new_file)
44
+ remove! unless blank?
45
+ end
46
+ end
47
+
48
+ ### Mount uploader
49
+
50
+ Add a string column to the model you want to mount the uploader on:
51
+
52
+ add_column :products, :image, :string
53
+
54
+ Open your model file and mount the uploader:
55
+
56
+ class Product
57
+ mount_before_save_uploader :image, ImageUploader
58
+ end
59
+
60
+ **NOTE!** You should use **`mount_before_save_uploader`** instead default `mount_uploader` method for YandexFotki storage.
61
+
62
+ ### Usage
63
+
64
+ Now you can assign files to the attribute, they will automatically be uploaded when the record is saved.
65
+
66
+ product = Product.new
67
+ product.image = params[:file]
68
+ product.save!
69
+ product.image.url # => 'http://img-fotki.yandex.ru/get/path/to/file_orig'
70
+
71
+ You can get different versions (sizes) of the same image from fotki.yandex.ru (**without need to resize images on your server**):
72
+
73
+ product.image.url # original size image url
74
+ product.image.url(:xl) # image fitted to 800x800 px square
75
+ product.image.url(100) # image fitted to 100x100 px square
76
+
77
+ All available sizes:
78
+
79
+ Size | Side of the bounding square, px
80
+ ---------------+----------------------------------
81
+ :orig or nil | Original size
82
+ :xl or 800 | 800
83
+ :l or 500 | 500
84
+ :m or 300 | 300
85
+ :s or 150 | 150
86
+ :xs or 100 | 100
87
+ :xxs or 75 | 75
88
+ :xxxs or 50 | 50
89
+
90
+
91
+ You can find this info on this page: [http://api.yandex.ru/fotki/doc/appendices/photo-storage.xml][photo-storage]
92
+
93
+ [photo-storage]: http://api.yandex.ru/fotki/doc/appendices/photo-storage.xml
94
+
95
+ Limitations
96
+ -----------
97
+
98
+ - Only ActiveRecord ORM supported now.
99
+ - Only JPEG, GIF, PNG and BMP images available
100
+ - Limited set of image sizes available
101
+
102
+ Resources
103
+ ---------
104
+
105
+ - [CarrierWave gem](https://github.com/jnicklas/carrierwave)
106
+ - [YandexFotki API documentation](http://api.yandex.ru/fotki/doc) on russian
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "yandexfotki/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "carrierwave-yandexfotki"
7
+ s.version = CarrierWave::YandexFotki::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Nickolay Abdrafikov"]
10
+ s.email = ["nicck.olay@gmail.com"]
11
+ s.homepage = ""
12
+ s.summary = %q{YandexFotki storage for CarrierWave gem}
13
+ s.description = %q{This gem provides a simple way to upload files from Rails application to fotki.yandex.ru service.}
14
+
15
+ s.rubyforge_project = "carrierwave-yandexfotki"
16
+
17
+ s.add_dependency('carrierwave', '>= 0.5.2')
18
+ s.add_dependency('nokogiri')
19
+ s.add_dependency('curb')
20
+ # s.add_dependency('multipart-post') # for net_http adapter
21
+
22
+ s.files = `git ls-files`.split("\n")
23
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
24
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
25
+ s.require_paths = ["lib"]
26
+ end
@@ -0,0 +1,13 @@
1
+ require "yandexfotki/file_url"
2
+ require "yandexfotki/activerecord"
3
+ require "yandexfotki/connection"
4
+
5
+ require "carrierwave/storage/yandexfotki"
6
+
7
+ ActiveRecord::Base.extend YandexFotki::ActiveRecord
8
+
9
+ CarrierWave::Uploader::Base.send :include, YandexFotki::FileUrl
10
+
11
+ CarrierWave::Uploader::Base.add_config :yandex_login
12
+ CarrierWave::Uploader::Base.add_config :yandex_password
13
+ CarrierWave::Uploader::Base.add_config :yandex_net_http
@@ -0,0 +1,114 @@
1
+ module CarrierWave
2
+ module Storage
3
+
4
+ class YandexFotki < Abstract
5
+ class File
6
+ def initialize(uploader, storage, identifier = nil)
7
+ @uploader = uploader
8
+ @storage = storage
9
+ @identifier = identifier
10
+ end
11
+
12
+ ##
13
+ # Remove the file from YandexFotki
14
+ #
15
+ def delete
16
+ if @identifier && @identifier['image_id'].present?
17
+ connection.delete_foto(@identifier['image_id'])
18
+ end
19
+ end
20
+
21
+ ##
22
+ # Returns the url on YandexFotki service
23
+ #
24
+ # === Returns
25
+ #
26
+ # [String] file's url
27
+ #
28
+ def url(style = nil)
29
+ if style
30
+ url_resized(style)
31
+ else
32
+ url_original
33
+ end
34
+ end
35
+
36
+ def store(file)
37
+ @identifier = connection.post_foto(file)
38
+ end
39
+
40
+ private
41
+
42
+ def url_resized(size)
43
+ if i = %w[800 500 300 150 100 75 50 ORIG ORIGINAL XL L M S XS XXS XXXS].index(size.to_s.upcase)
44
+ suffix = %w[XL L M S XS XXS XXXS orig orig XL L M S XS XXS XXXS][i]
45
+ else
46
+ return nil
47
+ end
48
+
49
+ url_original.gsub(/_[^_]+$/, "_#{suffix}")
50
+ end
51
+
52
+ def url_original
53
+ @identifier['image_url']
54
+ end
55
+
56
+ def connection
57
+ @storage.connection
58
+ end
59
+ end
60
+
61
+ ##
62
+ # Store the file on YandexFotki
63
+ #
64
+ # === Parameters
65
+ #
66
+ # [file (CarrierWave::SanitizedFile)] the file to store
67
+ #
68
+ # === Returns
69
+ #
70
+ # [YandexFotki::File] the stored file
71
+ #
72
+ def store!(file)
73
+ return if @image_identifier
74
+
75
+ f = CarrierWave::Storage::YandexFotki::File.new(uploader, self, @identifier)
76
+ @image_identifier = f.store(file)
77
+ f
78
+ end
79
+
80
+ # YAML serialized hash with image info: image_url and image_id
81
+ #
82
+ # @return [String]
83
+ #
84
+ def identifier
85
+ @image_identifier.to_yaml if @image_identifier.present?
86
+ end
87
+
88
+ # Do something to retrieve the file
89
+ #
90
+ # @param [String] identifier uniquely identifies the file
91
+ #
92
+ # [identifier (String)] uniquely identifies the file
93
+ #
94
+ # === Returns
95
+ #
96
+ # [YandexFotki::File] the stored file
97
+ #
98
+ def retrieve!(identifier)
99
+ identifier = YAML.load(identifier)
100
+ CarrierWave::Storage::YandexFotki::File.new(uploader, self, identifier)
101
+ end
102
+
103
+ def connection
104
+ @connection ||= ::YandexFotki::Connection.new(
105
+ :login => uploader.yandex_login,
106
+ :password => uploader.yandex_password,
107
+ :net_http => uploader.yandex_net_http
108
+ )
109
+ end
110
+
111
+ end # YandexFotki
112
+
113
+ end
114
+ end
@@ -0,0 +1,9 @@
1
+ module YandexFotki
2
+ module ActiveRecord
3
+ # Same as mount_uploader method but with before_save :store_image! callback
4
+ def mount_before_save_uploader(*args)
5
+ before_save "store_#{args.first.to_s}!"
6
+ mount_uploader(*args)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,107 @@
1
+ require "yandexfotki/encryptor"
2
+
3
+ module YandexFotki
4
+
5
+ class Connection
6
+ API_HOSTNAME = 'api-fotki.yandex.ru'
7
+
8
+ attr_reader :http
9
+
10
+ def initialize(credentials)
11
+ @login = credentials[:login]
12
+ @password = credentials[:password]
13
+
14
+ if credentials[:net_http]
15
+ require "yandexfotki/http_adapter/net_http"
16
+ @http = YandexFotki::HttpAdapter::NetHttp.new
17
+ else
18
+ require "yandexfotki/http_adapter/curb"
19
+ @http = YandexFotki::HttpAdapter::Curb.new
20
+ end
21
+ end
22
+
23
+ def post_foto(file)
24
+ image_id = api_post_foto(file)
25
+ image_url = api_get_foto_url(image_id)
26
+
27
+ return {'image_url' => image_url, 'image_id' => image_id}
28
+ end
29
+
30
+ def delete_foto(image_id)
31
+ resource_url = api_image_url(image_id)
32
+
33
+ http.delete(resource_url, authorization_header)
34
+ end
35
+
36
+ private
37
+
38
+ def authorization_header
39
+ { "Authorization" => %(FimpToken realm="fotki.yandex.ru", token="#{get_token}") }
40
+ end
41
+
42
+ def api_post_foto(file)
43
+ resource_url = "http://#{API_HOSTNAME}/post/"
44
+
45
+ response = http.post_multipart(resource_url, file, authorization_header)
46
+ image_id = response.split('=').last
47
+ end
48
+
49
+ def api_get_foto_info(image_id)
50
+ resource_url = api_image_url(image_id)
51
+
52
+ http.get(resource_url, authorization_header)
53
+ end
54
+
55
+ def api_set_foto_info(image_id, info_xml)
56
+ resource_url = api_image_url(image_id)
57
+
58
+ http.put(url, info_xml, authorization_header.merge("Content-Type" => 'application/atom+xml; type=entry'))
59
+ end
60
+
61
+ def get_token
62
+ @token ||= Rails.cache.fetch "YandexFotki:token:#{@login}" do
63
+ url = "http://auth.mobile.yandex.ru/yamrsa/key/"
64
+
65
+ key_xml = http.get(url)
66
+
67
+ xml = Nokogiri::XML(key_xml)
68
+
69
+ key = xml.xpath('/response/key').first.content
70
+ request_id = xml.xpath('/response/request_id').first.content
71
+
72
+ text = %(<credentials login="#{@login}" password="#{@password}"/>)
73
+ credentials = Encryptor.encrypt(key, text)
74
+
75
+ url = 'http://auth.mobile.yandex.ru/yamrsa/token/'
76
+ form_data = {'request_id' => request_id, 'credentials' => credentials}
77
+ token_xml_str = http.post_form(url, form_data)
78
+
79
+ token_xml = Nokogiri::XML(token_xml_str)
80
+ token_xml.xpath('/response/token').first.content
81
+ end
82
+ end
83
+
84
+ def api_image_url(image_id)
85
+ "http://#{API_HOSTNAME}/api/users/#{@login}/photo/#{image_id}/" if image_id.present?
86
+ end
87
+
88
+ def api_get_foto_url(image_id)
89
+ img_url = nil
90
+
91
+ attempts = 3
92
+ while img_url.nil? && attempts > 0 do
93
+ attempts -= 1
94
+
95
+ info_xml = api_get_foto_info(image_id)
96
+ content_tag = Nokogiri::XML(info_xml).css('entry>content')
97
+
98
+ img_url = content_tag.attribute('src').value rescue nil
99
+ end
100
+
101
+ raise "can't fetch src for foto ##{image_id}" if img_url.nil?
102
+
103
+ img_url
104
+ end
105
+ end
106
+
107
+ end
@@ -0,0 +1,63 @@
1
+ module YandexFotki
2
+
3
+ class Encryptor
4
+ class << self
5
+ def encrypt(key, data)
6
+ public_key, text = key, data
7
+
8
+ nstr, estr = public_key.split("#")
9
+ data_arr = []
10
+ text.each_byte {|b| data_arr << b}
11
+
12
+ n, e, step_size = nstr.hex, estr.hex, nstr.size/2-1
13
+
14
+ prev_crypted = Array.new(step_size, 0)
15
+
16
+ hex_out = ""
17
+
18
+ ((data_arr.size-1)/step_size+1).times do |i|
19
+ tmp = data_arr[i*step_size...(i+1)*step_size]
20
+
21
+ new_tmp = []
22
+ tmp.each_with_index {|t,ii| new_tmp << (t^prev_crypted[ii])}
23
+ tmp = new_tmp.reverse
24
+
25
+ plain = 0
26
+ tmp.each_with_index {|t,ii| plain += t*(256**ii%n)}
27
+ hex_result = "%x" % power_modulo(plain, e, n) # plain ** e % n
28
+ hex_result = hex_result.rjust(nstr.size, '0')
29
+
30
+ (0...[hex_result.size, prev_crypted.size*2].min).step(2) do |x|
31
+ prev_crypted[x/2] = hex_result[x...(x+2)].hex
32
+ end
33
+
34
+ hex_out += (tmp.size < 16 ? "0" : "") + ("%x" % tmp.size) + "00"
35
+ ks = nstr.size/2
36
+
37
+ hex_out += (ks < 16 ? "0" : "") + ("%x" % ks) + "00"
38
+ hex_out += hex_result
39
+ end
40
+
41
+ hs = []
42
+ hex_out.scan(/../).each{|x| hs << x.hex.chr}
43
+ hs = hs.join
44
+ Base64.encode64(hs).gsub("\n","")
45
+ end
46
+
47
+ private
48
+
49
+ # http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/71964
50
+ def power_modulo(b, p, m)
51
+ if p == 1
52
+ b % m
53
+ elsif (p & 0x1) == 0 # p.even?
54
+ t = power_modulo(b, p >> 1, m)
55
+ (t * t) % m
56
+ else
57
+ (b * power_modulo(b, p-1, m)) % m
58
+ end
59
+ end
60
+ end
61
+ end
62
+
63
+ end
@@ -0,0 +1,9 @@
1
+ module YandexFotki
2
+
3
+ module FileUrl
4
+ def url(*args)
5
+ file.url(*args) || super
6
+ end
7
+ end
8
+
9
+ end
@@ -0,0 +1,51 @@
1
+ require "curb"
2
+
3
+ module YandexFotki
4
+ module HttpAdapter
5
+
6
+ class Curb
7
+ def get(url, headers = {})
8
+ curl = Curl::Easy.new(url)
9
+ curl.headers["Authorization"] = headers['Authorization'] if headers['Authorization']
10
+ curl.http_get
11
+ curl.body_str
12
+ end
13
+
14
+ def delete(url, headers)
15
+ curl = Curl::Easy.new(url)
16
+ curl.headers["Authorization"] = headers['Authorization']
17
+ curl.http_delete
18
+ curl.body_str
19
+ end
20
+
21
+ def post_form(url, form_data)
22
+ curl = Curl::Easy.new(url)
23
+ curl.http_post(
24
+ Curl::PostField.content('request_id', form_data['request_id']),
25
+ Curl::PostField.content('credentials', form_data['credentials'])
26
+ )
27
+ curl.body_str
28
+ end
29
+
30
+ def post_multipart(url, file, headers)
31
+ curl = Curl::Easy.new(url)
32
+ curl.headers["Authorization"] = headers['Authorization']
33
+ curl.multipart_form_post = true
34
+ curl.http_post(
35
+ Curl::PostField.file('image', file.path, file.filename),
36
+ Curl::PostField.content('yaru', '0')
37
+ )
38
+ curl.body_str
39
+ end
40
+
41
+ def put(url, data, headers)
42
+ curl = Curl::Easy.new(url)
43
+ curl.headers["Authorization"] = headers['Authorization']
44
+ curl.headers["Content-Type"] = headers['Content-Type']
45
+ curl.http_put(data)
46
+ curl.body_str
47
+ end
48
+ end
49
+
50
+ end
51
+ end
@@ -0,0 +1,68 @@
1
+ require "net/http"
2
+ require "net/http/post/multipart" # multipart-post gem
3
+
4
+ module YandexFotki
5
+ module HttpAdapter
6
+
7
+ class NetHttp
8
+ def get(url, headers = {})
9
+ url = URI.parse(url)
10
+
11
+ http = Net::HTTP.new(url.host)
12
+ request = Net::HTTP::Get.new(url.path)
13
+ request["Authorization"] = headers['Authorization'] if headers['Authorization']
14
+
15
+ response = http.request(request)
16
+ response.body
17
+ end
18
+
19
+ def delete(url, headers)
20
+ url = URI.parse(url)
21
+
22
+ http = Net::HTTP.new(url.host)
23
+ request = Net::HTTP::Delete.new(url.path)
24
+ request["Authorization"] = headers['Authorization']
25
+
26
+ response = http.request(request)
27
+ response.body
28
+ end
29
+
30
+ def post_form(url, form_data)
31
+ url = URI.parse(url)
32
+
33
+ response = Net::HTTP.post_form(url, form_data)
34
+ response.body
35
+ end
36
+
37
+ def post_multipart(url, file, headers)
38
+ url = URI.parse(url)
39
+
40
+ file_io = ::File.open(file.path)
41
+ form_data = {
42
+ "image" => UploadIO.new(file_io, file.content_type, file.filename),
43
+ "yaru" => 0
44
+ }
45
+
46
+ http = Net::HTTP.new(url.host)
47
+ request = Net::HTTP::Post::Multipart.new(url.path, form_data)
48
+ request["Authorization"] = headers['Authorization']
49
+
50
+ response = http.request(request, nil)
51
+ response.body
52
+ end
53
+
54
+ def put(url, data, headers)
55
+ url = URI.parse(url)
56
+
57
+ http = Net::HTTP.new(url.host)
58
+ request = Net::HTTP::Put.new(url.path)
59
+ request["Authorization"] = headers['Authorization']
60
+ request['Content-Type'] = headers['Content-Type']
61
+
62
+ response = http.request(request, data)
63
+ response.body
64
+ end
65
+ end
66
+
67
+ end
68
+ end
@@ -0,0 +1,5 @@
1
+ module CarrierWave
2
+ module YandexFotki
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,102 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: carrierwave-yandexfotki
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.1
6
+ platform: ruby
7
+ authors:
8
+ - Nickolay Abdrafikov
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-02-23 00:00:00 +03:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: carrierwave
18
+ prerelease: false
19
+ requirement: &id001 !ruby/object:Gem::Requirement
20
+ none: false
21
+ requirements:
22
+ - - ">="
23
+ - !ruby/object:Gem::Version
24
+ version: 0.5.2
25
+ type: :runtime
26
+ version_requirements: *id001
27
+ - !ruby/object:Gem::Dependency
28
+ name: nokogiri
29
+ prerelease: false
30
+ requirement: &id002 !ruby/object:Gem::Requirement
31
+ none: false
32
+ requirements:
33
+ - - ">="
34
+ - !ruby/object:Gem::Version
35
+ version: "0"
36
+ type: :runtime
37
+ version_requirements: *id002
38
+ - !ruby/object:Gem::Dependency
39
+ name: curb
40
+ prerelease: false
41
+ requirement: &id003 !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: "0"
47
+ type: :runtime
48
+ version_requirements: *id003
49
+ description: This gem provides a simple way to upload files from Rails application to fotki.yandex.ru service.
50
+ email:
51
+ - nicck.olay@gmail.com
52
+ executables: []
53
+
54
+ extensions: []
55
+
56
+ extra_rdoc_files: []
57
+
58
+ files:
59
+ - .gitignore
60
+ - Gemfile
61
+ - README.md
62
+ - Rakefile
63
+ - carrierwave-yandexfotki.gemspec
64
+ - lib/carrierwave-yandexfotki.rb
65
+ - lib/carrierwave/storage/yandexfotki.rb
66
+ - lib/yandexfotki/activerecord.rb
67
+ - lib/yandexfotki/connection.rb
68
+ - lib/yandexfotki/encryptor.rb
69
+ - lib/yandexfotki/file_url.rb
70
+ - lib/yandexfotki/http_adapter/curb.rb
71
+ - lib/yandexfotki/http_adapter/net_http.rb
72
+ - lib/yandexfotki/version.rb
73
+ has_rdoc: true
74
+ homepage: ""
75
+ licenses: []
76
+
77
+ post_install_message:
78
+ rdoc_options: []
79
+
80
+ require_paths:
81
+ - lib
82
+ required_ruby_version: !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: "0"
88
+ required_rubygems_version: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ version: "0"
94
+ requirements: []
95
+
96
+ rubyforge_project: carrierwave-yandexfotki
97
+ rubygems_version: 1.5.0
98
+ signing_key:
99
+ specification_version: 3
100
+ summary: YandexFotki storage for CarrierWave gem
101
+ test_files: []
102
+