ink_file_picker 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d22df05e49b4463a633bb87fbb978dae55653c9f
4
+ data.tar.gz: 590c40f9b6e7f1ff9f0d2669a29e68d7f6a9f64d
5
+ SHA512:
6
+ metadata.gz: ee4b1ade5cb8f4ab41017a119384cc75029d13f11d338b9262096e3238c88ccc40cae29f6c0db0ecd39d077f666e5c7c5b4410ea6b1b972e53b2b3269f8422b8
7
+ data.tar.gz: d22c324c7ad0b417bdb0e32883c7190f00c3571ed9d68dd4e1bb0ec3c671b4096aa18b8821ff3613e1d6b8ff4cdc3877bcb1cb1cbe9627fc2245d7625dfa6e50
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ bin/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in ink_file_picker.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Thorbjørn Hermansen
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,76 @@
1
+ # InkFilePicker
2
+
3
+ Ruby API client for Ink File Picker (known as filepicker.io).
4
+
5
+
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'ink_file_picker'
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install ink_file_picker
20
+
21
+
22
+
23
+ ## Usage
24
+
25
+ This client mirrors part of File Picker's JavaScript API.
26
+
27
+
28
+
29
+ ### Creating a client
30
+
31
+ ```ruby
32
+ # Create a client which will sign URLs. You may drop secret if you have
33
+ # not enabled this feature in your developer portal for your application.
34
+ client = InkFilePicker.client(key: 'you-api-key', secret: 'your-secret')
35
+ ```
36
+
37
+ ### Storing a file
38
+ ```ruby
39
+ response = client.store_file file_or_path, content_type
40
+ response = client.store_url 'http://www.example.com/img.jpg'
41
+ ```
42
+
43
+ ### Removing a file
44
+ ```ruby
45
+ response = client.remove url_or_handle_name
46
+ ```
47
+
48
+ ### Read operations
49
+ ```ruby
50
+ url = client.convert_url url_or_handle_name, w: 100, h: 100
51
+ url = client.convert_url url_or_handle_name, {w: 100, h: 100}, expiry: 10.minutes.from_now.to_i
52
+
53
+ # Adds policy and signature, if secret given when client was created.
54
+ url = client.retrieve_url url_or_handle_name
55
+ url = client.retrieve_url url_or_handle_name, expiry: 10.minutes.from_now.to_i
56
+
57
+
58
+ # Get simple stat on a file, like the Javascript client
59
+ stat = client.stat url_or_handle_name
60
+
61
+ dimentions = client.stat url_or_handle_name, {width: true, height: true}
62
+ ```
63
+
64
+ ### Errors
65
+
66
+ When making requests to the API errors may occur. `InkFilePicker::ClientError` or `InkFilePicker::ServerError` will
67
+ be raised if we are getting 4xx or 5xx responses back from File Picker. All errors inherits from `InkFilePicker::Error`.
68
+
69
+
70
+ ## Contributing
71
+
72
+ 1. Fork it (https://github.com/Skalar/ink_file_picker/fork)
73
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
74
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
75
+ 4. Push to the branch (`git push origin my-new-feature`)
76
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'ink_file_picker/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "ink_file_picker"
8
+ spec.version = InkFilePicker::VERSION
9
+ spec.authors = ["Thorbjørn Hermansen"]
10
+ spec.email = ["thhermansen@gmail.com"]
11
+ spec.summary = %q{Client for Ink File Picker}
12
+ #spec.description = %q{TODO: Write a longer description. Optional.}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "activesupport", ">= 3.2.14", "< 5"
22
+ spec.add_dependency "faraday", "~> 0.9.0"
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.5"
25
+ spec.add_development_dependency "rake"
26
+ spec.add_development_dependency "rspec", "~> 2.14.1"
27
+ end
@@ -0,0 +1,21 @@
1
+ module InkFilePicker
2
+ module Assignable
3
+
4
+ def []=(name, value)
5
+ public_send "#{name}=", value
6
+ end
7
+
8
+ def [](name)
9
+ public_send name
10
+ end
11
+
12
+
13
+ private
14
+
15
+ def assign(attributes)
16
+ attributes.each_pair do |name, value|
17
+ self[name] = value
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,183 @@
1
+ require "faraday"
2
+
3
+ module InkFilePicker
4
+ class Client
5
+ attr_accessor :configuration
6
+
7
+ def initialize(configuration)
8
+ self.configuration = Configuration.new configuration
9
+ end
10
+
11
+
12
+ # Public: Store a file from given URL.
13
+ #
14
+ # url - URL to resource
15
+ # policy_attributes - If you use security policies you may send in for instance {expire: 10.minutes.from_now} here
16
+ #
17
+ # Returns a hash representing the response where you can read for instance 'url'
18
+ def store_url(url, policy_attributes = {})
19
+ params = {key: configuration.key}
20
+
21
+ add_policy_to params, from: policy_attributes, ensure_included: {call: 'store'}
22
+
23
+ response = http_connection.post configuration.store_path do |request|
24
+ request.params = params
25
+ request.body = {url: url}
26
+ end
27
+
28
+ wrap_response_or_fail_unless_success! response
29
+ end
30
+
31
+ # Public: Store a file from given local file or path.
32
+ #
33
+ # file_or_path - File or path to file
34
+ # content_type - The file's content type
35
+ # filename - The file's name, optional
36
+ # policy_attributes - If you use security policies you may send in for instance {expire: 10.minutes.from_now} here
37
+ #
38
+ # Returns a hash representing the response where you can read for instance 'url'
39
+ def store_file(file_or_path, content_type, filename = nil, policy_attributes = {})
40
+ file_upload = Faraday::UploadIO.new file_or_path, content_type, filename
41
+ params = {key: configuration.key}
42
+
43
+ add_policy_to params, from: policy_attributes, ensure_included: {call: 'store'}
44
+
45
+ response = http_connection.post configuration.store_path do |request|
46
+ request.params = params
47
+ request.body = {fileUpload: file_upload}
48
+ end
49
+
50
+ wrap_response_or_fail_unless_success! response
51
+ end
52
+
53
+ # Public: Removes a file from file picker.
54
+ #
55
+ # handle_or_url - The handle or URL to the file
56
+ # policy_attributes - If you use security policies you may send in for instance {expire: 10.minutes.from_now} here
57
+ #
58
+ # Returns boolean value
59
+ def remove(handle_or_url, policy_attributes = {})
60
+ response = http_connection.delete remove_url(handle_or_url, policy_attributes)
61
+
62
+ wrap_response_or_fail_unless_success! response
63
+ end
64
+
65
+
66
+ # Public: Returns short stat for a file
67
+ #
68
+ # handle_or_url - The handle or URL to the file
69
+ # params - Request params, like {width: true, height: true} to get width and height info. May be empty for default response
70
+ # policy_attributes - If you use security policies you may send in for instance {expire: 10.minutes.from_now} here
71
+ #
72
+ # Returns hash of headers returned from file picker or false if request was unsuccessful
73
+ def stat(handle_or_url, params = {}, policy_attributes = {})
74
+ response = http_connection.get stat_url(handle_or_url, params, policy_attributes)
75
+
76
+ wrap_response_or_fail_unless_success! response
77
+ end
78
+
79
+ # Public: Generates a you can use for removing an asset on file picker.
80
+ #
81
+ # handle_or_url - The handle or URL to the file
82
+ # policy_attributes - If you use security policies you may send in for instance {expire: 10.minutes.from_now} here
83
+ #
84
+ # Returns a URL to the converted image
85
+ def remove_url(handle_or_url, policy_attributes = {})
86
+ generate_url handle_or_url, {key: configuration.key}, policy_attributes, call: 'remove'
87
+ end
88
+
89
+ # Public: Generates a convert URL for given file.
90
+ #
91
+ # handle_or_url - The handle or URL to the file
92
+ # params - Convert params, like {w: 100, h:100}
93
+ # policy_attributes - If you use security policies you may send in for instance {expire: 10.minutes.from_now} here
94
+ #
95
+ # Returns a URL to the converted image
96
+ def convert_url(handle_or_url, params = {}, policy_attributes = {})
97
+ generate_url handle_or_url, params, policy_attributes, call: 'convert', url_action: 'convert'
98
+ end
99
+
100
+
101
+ # Public: Generates a URL for a given file
102
+ #
103
+ # handle_or_url - The handle or URL to the file
104
+ # params - Params to be added as get params, like {cache: true}
105
+ # policy_attributes - If you use security policies you may send in for instance {expire: 10.minutes.from_now} here
106
+ #
107
+ # This method is not that usefull unless you have enabled security policy
108
+ #
109
+ # Returns a URL to the image
110
+ def retrieve_url(handle_or_url, params = {}, policy_attributes = {})
111
+ generate_url handle_or_url, params, policy_attributes, call: 'read'
112
+ end
113
+
114
+
115
+ # Public: Generates a stat URL for a given file
116
+ #
117
+ # handle_or_url - The handle or URL to the file
118
+ # policy_attributes - If you use security policies you may send in for instance {expire: 10.minutes.from_now} here
119
+ #
120
+ # Returns a URL to the image you can do a HEAD request to in order to get stats
121
+ def stat_url(handle_or_url, params, policy_attributes = {})
122
+ generate_url handle_or_url, params, policy_attributes, call: 'stat', url_action: 'metadata'
123
+ end
124
+
125
+
126
+
127
+
128
+
129
+ # Public: Creates a policy with default configuration set in this client.
130
+ #
131
+ # Returns Policy object
132
+ def policy(attributes)
133
+ attributes.reverse_merge!(
134
+ secret: configuration.secret,
135
+ expiry: Time.now.to_i + configuration.default_expiry
136
+ )
137
+
138
+ Policy.new attributes
139
+ end
140
+
141
+
142
+
143
+
144
+
145
+ def http_connection
146
+ @http_connection ||= Faraday.new(url: configuration.filepicker_url) do |builder|
147
+ builder.request :multipart
148
+ builder.request :url_encoded
149
+ builder.adapter configuration.http_adapter || Faraday.default_adapter
150
+ end
151
+ end
152
+
153
+
154
+ private
155
+
156
+ def generate_url(handle_or_url, params, policy_attributes, options)
157
+ file_handle = FileHandle.new handle_or_url, configuration.cdn_url
158
+
159
+ add_policy_to params, from: policy_attributes, ensure_included: {handle: file_handle.handle, call: options[:call]}
160
+
161
+ url = UrlBuilder.new(file_url: file_handle.url, action: options[:url_action], params: params).to_s
162
+ end
163
+
164
+ def add_policy_to(params, options = {})
165
+ policy_attributes = (options[:from] || {}).merge options[:ensure_included]
166
+ params.merge! policy(policy_attributes)
167
+ end
168
+
169
+ # Private: Inspects response for error and raise a InkFilePicker error if client/server error.
170
+ def wrap_response_or_fail_unless_success!(response)
171
+ case response.status
172
+ when 200...300
173
+ Response.new response
174
+ when 400...500
175
+ fail ClientError.new response.body, response
176
+ when 500...600
177
+ fail ServerError.new response.body, response
178
+ else
179
+ fail Error, "Response was neither a success, nor within http status 400...600. Response was: '#{response.inspect}'."
180
+ end
181
+ end
182
+ end
183
+ end
@@ -0,0 +1,32 @@
1
+ module InkFilePicker
2
+ class Configuration
3
+ include Assignable
4
+
5
+ API_DEFAULTS = {
6
+ secret: nil,
7
+ default_expiry: 600, # in 10 hours
8
+ cdn_url: 'https://www.filepicker.io/api/file/',
9
+ filepicker_url: 'https://www.filepicker.io',
10
+ store_path: '/api/store/S3',
11
+ http_adapter: :net_http
12
+ }
13
+
14
+ attr_accessor :key, :secret, :default_expiry, :cdn_url, :filepicker_url, :store_path, :http_adapter
15
+
16
+ def initialize(attributes = {})
17
+ assign API_DEFAULTS
18
+ assign attributes
19
+
20
+ verify!
21
+ end
22
+
23
+
24
+ private
25
+
26
+ def verify!
27
+ if key.blank?
28
+ fail ArgumentError, "An API key must be provided"
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,36 @@
1
+ module InkFilePicker
2
+ module ErrorWithOriginal
3
+ attr_reader :msg, :related
4
+
5
+ def initialize(msg, related)
6
+ @msg = msg
7
+ @related = related
8
+ end
9
+
10
+ def to_s
11
+ "#{self.class.name}: Message: '#{msg}'. Related object: '#{related.inspect}'."
12
+ end
13
+ alias inspect to_s
14
+ end
15
+
16
+
17
+ # Public: Base class for errors related to InkFilePicker.
18
+ class Error < StandardError
19
+ end
20
+
21
+ # Public: Got a request error back trying to do a request
22
+ #
23
+ # This includes wire errors like timeouts etc, and server errors
24
+ # like 5xx. Inspect error_or_response for more information.
25
+ #
26
+ class ServerError < Error
27
+ include ErrorWithOriginal
28
+ end
29
+
30
+ # Public: Got an error where the client seems to be doing something wrong
31
+ #
32
+ # These errors mainly comes from http 4xx errors.
33
+ class ClientError < Error
34
+ include ErrorWithOriginal
35
+ end
36
+ end
@@ -0,0 +1,32 @@
1
+ require 'uri'
2
+
3
+ module InkFilePicker
4
+ # Public: Simple class for working with file URL and file handles.
5
+ #
6
+ # Does conversions like url to handle and from a handle to url
7
+ class FileHandle
8
+ attr_accessor :handle, :url, :cdn_url
9
+
10
+ def initialize(handle_or_url, cdn_url)
11
+ self.cdn_url = cdn_url
12
+ self.handle = extract_handle handle_or_url
13
+ self.url = build_url_from_handle
14
+ end
15
+
16
+
17
+
18
+ private
19
+
20
+ def build_url_from_handle
21
+ joins_with = cdn_url.ends_with?('/') ? '' : '/'
22
+ [cdn_url, handle].join joins_with
23
+ end
24
+
25
+ def extract_handle(handle_or_url)
26
+ uri = URI.parse handle_or_url
27
+ uri.path.split('/').last
28
+ rescue URI::InvalidURIError
29
+ handle_or_url
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,50 @@
1
+ require 'base64'
2
+ require 'openssl'
3
+
4
+ module InkFilePicker
5
+ class Policy
6
+ include Assignable
7
+
8
+ POLICY_ATTRIBUTES = %w[expiry call handle max_size min_size path container].freeze
9
+
10
+ attr_accessor :secret, *POLICY_ATTRIBUTES
11
+
12
+ def initialize(attributes)
13
+ assign attributes
14
+ end
15
+
16
+
17
+
18
+ def policy
19
+ Base64.urlsafe_encode64 policy_json
20
+ end
21
+
22
+ def signature
23
+ OpenSSL::HMAC.hexdigest 'sha256', secret, policy
24
+ end
25
+
26
+
27
+ def to_hash
28
+ return {} if secret.blank?
29
+
30
+ {
31
+ policy: policy,
32
+ signature: signature
33
+ }
34
+ end
35
+
36
+
37
+
38
+ def policy_json
39
+ out = {}
40
+
41
+ POLICY_ATTRIBUTES.each do |attr_name|
42
+ if value = self[attr_name] and value.present?
43
+ out[attr_name] = value
44
+ end
45
+ end
46
+
47
+ out.to_json
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,23 @@
1
+ module InkFilePicker
2
+ # Public: Simple decorator class for response.
3
+ #
4
+ # Decorates the response with hash like access to the
5
+ # parsed body, which is expected to be JSON.
6
+ class Response
7
+ attr_reader :http_response
8
+
9
+ delegate :success?, to: :http_response
10
+
11
+ def initialize(http_response)
12
+ @http_response = http_response
13
+ end
14
+
15
+ def [](key)
16
+ parsed_body[key.to_s]
17
+ end
18
+
19
+ def parsed_body
20
+ @parsed_body ||= JSON.parse http_response.body
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,20 @@
1
+ module InkFilePicker
2
+ # Public: Takes a file url, adds action to the path (if any), and includes params.
3
+ class UrlBuilder
4
+ include Assignable
5
+
6
+ attr_accessor :file_url, :action, :params
7
+
8
+ def initialize(attributes = {})
9
+ assign attributes
10
+ end
11
+
12
+ def url
13
+ url = [file_url, action].compact.join '/'
14
+ url = [url, params.to_param].join '?' if params.any?
15
+
16
+ url
17
+ end
18
+ alias to_s url
19
+ end
20
+ end
@@ -0,0 +1,3 @@
1
+ module InkFilePicker
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,25 @@
1
+ require "ink_file_picker/version"
2
+
3
+ require "active_support/all"
4
+ require "ink_file_picker/errors"
5
+
6
+ module InkFilePicker
7
+ extend ActiveSupport::Autoload
8
+
9
+ autoload :Assignable
10
+ autoload :Configuration
11
+ autoload :FileHandle
12
+ autoload :UrlBuilder
13
+ autoload :Response
14
+ autoload :Client
15
+ autoload :Policy
16
+
17
+ # Public: Creates a new Ink File Picker Client.
18
+ #
19
+ # configuration - configuration for the client with an API key
20
+ #
21
+ # Returns InkFilePicker::Client
22
+ def self.client(configuration)
23
+ Client.new configuration
24
+ end
25
+ end
Binary file
@@ -0,0 +1,410 @@
1
+ require 'spec_helper'
2
+
3
+ describe InkFilePicker::Client do
4
+ let(:attributes) do
5
+ {
6
+ key: 'key',
7
+ secret: '6U5CWAU57NAHDC2ICXQKMXYZ4Q',
8
+ http_adapter: :test
9
+ }
10
+ end
11
+
12
+ subject { described_class.new attributes }
13
+
14
+ describe "#store_url" do
15
+ let(:url) { 'https://s3.amazonaws.com/test.jpg' }
16
+ let(:response) { '{"url": "https://www.filepicker.io/api/file/WmFxB2aSe20SGT2kzSsr", "size": 234, "type": "image/jpeg", "filename": "test.jpg", "key": "WmFxB2aSe20SGT2kzSsr_test.jpg"}' }
17
+
18
+ context "without secret" do
19
+ before { subject.configuration.secret = nil }
20
+
21
+ it "posts to filepicker" do
22
+ stubs = Faraday::Adapter::Test::Stubs.new do |stub|
23
+ stub.post(subject.configuration.store_path + '?key=key', {url: url}) { [200, {}, response] }
24
+ end
25
+
26
+ stubbed_connection = Faraday.new do |builder|
27
+ builder.adapter :test, stubs
28
+ end
29
+
30
+ subject.stub(:http_connection).and_return stubbed_connection
31
+
32
+ response = subject.store_url url
33
+
34
+ stubs.verify_stubbed_calls
35
+ expect(response['url']).to eq 'https://www.filepicker.io/api/file/WmFxB2aSe20SGT2kzSsr'
36
+ expect(response[:url]).to eq 'https://www.filepicker.io/api/file/WmFxB2aSe20SGT2kzSsr'
37
+ expect(response.http_response).to be_a Faraday::Response
38
+ end
39
+ end
40
+
41
+ context "with secret" do
42
+ it "includes policy and signature" do
43
+ stubs = Faraday::Adapter::Test::Stubs.new do |stub|
44
+ store_path = subject.configuration.store_path + '?key=key&policy=eyJleHBpcnkiOjEzOTQzNjM4OTYsImNhbGwiOiJzdG9yZSJ9&signature=60cb43bb945543d7fdbd2662ae21d5c53e28529720263619cfebc3509e820807'
45
+ stub.post(store_path, {url: url}) { [200, {}, response] }
46
+ end
47
+
48
+ stubbed_connection = Faraday.new do |builder|
49
+ builder.adapter :test, stubs
50
+ end
51
+
52
+ subject.stub(:http_connection).and_return stubbed_connection
53
+
54
+ response = subject.store_url url, expiry: 1394363896
55
+
56
+ stubs.verify_stubbed_calls
57
+ expect(response['url']).to eq 'https://www.filepicker.io/api/file/WmFxB2aSe20SGT2kzSsr'
58
+ end
59
+
60
+ it "handles client errors correctly" do
61
+ stubs = Faraday::Adapter::Test::Stubs.new do |stub|
62
+ store_path = subject.configuration.store_path + '?key=key&policy=eyJleHBpcnkiOjEzOTQzNjM4OTYsImNhbGwiOiJzdG9yZSJ9&signature=60cb43bb945543d7fdbd2662ae21d5c53e28529720263619cfebc3509e820807'
63
+ stub.post(store_path, {url: url}) { [403, {}, '[uuid=AF614DF7F9594A87] This action has been secured by the developer of this website. Error: The signature was not valid'] }
64
+ end
65
+
66
+ stubbed_connection = Faraday.new do |builder|
67
+ builder.adapter :test, stubs
68
+ end
69
+
70
+ subject.stub(:http_connection).and_return stubbed_connection
71
+
72
+ expect { subject.store_url url, expiry: 1394363896 }.to raise_error InkFilePicker::ClientError
73
+ end
74
+
75
+ it "handles server errors correctly" do
76
+ stubs = Faraday::Adapter::Test::Stubs.new do |stub|
77
+ store_path = subject.configuration.store_path + '?key=key&policy=eyJleHBpcnkiOjEzOTQzNjM4OTYsImNhbGwiOiJzdG9yZSJ9&signature=60cb43bb945543d7fdbd2662ae21d5c53e28529720263619cfebc3509e820807'
78
+ stub.post(store_path, {url: url}) { [502, {}, 'Bad Gateway'] }
79
+ end
80
+
81
+ stubbed_connection = Faraday.new do |builder|
82
+ builder.adapter :test, stubs
83
+ end
84
+
85
+ subject.stub(:http_connection).and_return stubbed_connection
86
+
87
+ expect { subject.store_url url, expiry: 1394363896 }.to raise_error InkFilePicker::ServerError
88
+ end
89
+ end
90
+ end
91
+
92
+ describe "#store_file" do
93
+ let(:path) { File.join(File.dirname(__FILE__), '../fixtures', 'skalar.png') }
94
+ let(:file) { File.open path }
95
+ let!(:file_upload) { Faraday::UploadIO.new file, 'image/png' }
96
+ let(:response) { '{"url": "https://www.filepicker.io/api/file/WmFxB2aSe20SGT2kzSsr", "size": 234, "type": "image/jpeg", "filename": "test.jpg", "key": "WmFxB2aSe20SGT2kzSsr_test.jpg"}' }
97
+
98
+ context "without secret" do
99
+ before { subject.configuration.secret = nil }
100
+
101
+ it "uploads the given file as file" do
102
+ stubs = Faraday::Adapter::Test::Stubs.new do |stub|
103
+ stub.post(subject.configuration.store_path + '?key=key', {fileUpload: file_upload}) { [200, {}, response] }
104
+ end
105
+
106
+ stubbed_connection = Faraday.new do |builder|
107
+ builder.adapter :test, stubs
108
+ end
109
+
110
+ subject.stub(:http_connection).and_return stubbed_connection
111
+ Faraday::UploadIO.stub(:new).and_return file_upload # Need same object, so request equals the stub
112
+
113
+ response = subject.store_file file, 'image/png'
114
+
115
+ stubs.verify_stubbed_calls
116
+ expect(response['url']).to eq 'https://www.filepicker.io/api/file/WmFxB2aSe20SGT2kzSsr'
117
+ end
118
+
119
+ it "uploads the given file as path" do
120
+ stubs = Faraday::Adapter::Test::Stubs.new do |stub|
121
+ stub.post(subject.configuration.store_path + '?key=key', {fileUpload: file_upload}) { [200, {}, response] }
122
+ end
123
+
124
+ stubbed_connection = Faraday.new do |builder|
125
+ builder.adapter :test, stubs
126
+ end
127
+
128
+ subject.stub(:http_connection).and_return stubbed_connection
129
+ Faraday::UploadIO.stub(:new).and_return file_upload # Need same object, so request equals the stub
130
+
131
+ response = subject.store_file path, 'image/png'
132
+
133
+ stubs.verify_stubbed_calls
134
+ expect(response['url']).to eq 'https://www.filepicker.io/api/file/WmFxB2aSe20SGT2kzSsr'
135
+ end
136
+ end
137
+
138
+ context "with secret" do
139
+ it "uploads the given file as file" do
140
+ stubs = Faraday::Adapter::Test::Stubs.new do |stub|
141
+ stub.post(subject.configuration.store_path + '?key=key&policy=eyJleHBpcnkiOjEzOTQzNjM4OTYsImNhbGwiOiJzdG9yZSJ9&signature=60cb43bb945543d7fdbd2662ae21d5c53e28529720263619cfebc3509e820807', {fileUpload: file_upload}) { [200, {}, response] }
142
+ end
143
+
144
+ stubbed_connection = Faraday.new do |builder|
145
+ builder.adapter :test, stubs
146
+ end
147
+
148
+ subject.stub(:http_connection).and_return stubbed_connection
149
+ Faraday::UploadIO.stub(:new).and_return file_upload # Need same object, so request equals the stub
150
+
151
+ response = subject.store_file file, 'image/png', nil, expiry: 1394363896
152
+
153
+ stubs.verify_stubbed_calls
154
+ expect(response['url']).to eq 'https://www.filepicker.io/api/file/WmFxB2aSe20SGT2kzSsr'
155
+ end
156
+
157
+ it "handles client errors correctly" do
158
+ stubs = Faraday::Adapter::Test::Stubs.new do |stub|
159
+ store_path = subject.configuration.store_path + '?key=key&policy=eyJleHBpcnkiOjEzOTQzNjM4OTYsImNhbGwiOiJzdG9yZSJ9&signature=60cb43bb945543d7fdbd2662ae21d5c53e28529720263619cfebc3509e820807'
160
+ stub.post(store_path) { [403, {}, '[uuid=AF614DF7F9594A87] This action has been secured by the developer of this website. Error: The signature was not valid'] }
161
+ end
162
+
163
+ stubbed_connection = Faraday.new do |builder|
164
+ builder.adapter :test, stubs
165
+ end
166
+
167
+ subject.stub(:http_connection).and_return stubbed_connection
168
+
169
+ expect { subject.store_file file, 'image/png', nil, expiry: 1394363896 }.to raise_error InkFilePicker::ClientError
170
+ end
171
+
172
+ it "handles server errors correctly" do
173
+ stubs = Faraday::Adapter::Test::Stubs.new do |stub|
174
+ store_path = subject.configuration.store_path + '?key=key&policy=eyJleHBpcnkiOjEzOTQzNjM4OTYsImNhbGwiOiJzdG9yZSJ9&signature=60cb43bb945543d7fdbd2662ae21d5c53e28529720263619cfebc3509e820807'
175
+ stub.post(store_path) { [502, {}, 'Bad Gateway'] }
176
+ end
177
+
178
+ stubbed_connection = Faraday.new do |builder|
179
+ builder.adapter :test, stubs
180
+ end
181
+
182
+ subject.stub(:http_connection).and_return stubbed_connection
183
+
184
+ expect { subject.store_file file, 'image/png', nil, expiry: 1394363896 }.to raise_error InkFilePicker::ServerError
185
+ end
186
+ end
187
+ end
188
+
189
+ describe "#remove" do
190
+ let(:file_url) { 'https://www.filepicker.io/api/file/WmFxB2aSe20SGT2kzSsr' }
191
+
192
+ context "without secret" do
193
+ before { subject.configuration.secret = nil }
194
+
195
+ it "makes a delete request with url" do
196
+ stubs = Faraday::Adapter::Test::Stubs.new do |stub|
197
+ stub.delete(file_url + '?key=key') { [200, {}, 'success'] }
198
+ end
199
+
200
+ stubbed_connection = Faraday.new do |builder|
201
+ builder.adapter :test, stubs
202
+ end
203
+
204
+ subject.stub(:http_connection).and_return stubbed_connection
205
+
206
+ response = subject.remove file_url
207
+
208
+ stubs.verify_stubbed_calls
209
+ expect(response).to be_true
210
+ end
211
+
212
+ it "makes delete request with file handle name" do
213
+ stubs = Faraday::Adapter::Test::Stubs.new do |stub|
214
+ stub.delete(file_url + '?key=key') { [200, {}, 'success'] }
215
+ end
216
+
217
+ stubbed_connection = Faraday.new do |builder|
218
+ builder.adapter :test, stubs
219
+ end
220
+
221
+ subject.stub(:http_connection).and_return stubbed_connection
222
+
223
+ response = subject.remove 'WmFxB2aSe20SGT2kzSsr'
224
+
225
+ stubs.verify_stubbed_calls
226
+ expect(response).to be_true
227
+ end
228
+
229
+ it "handles server errors correctly" do
230
+ stubs = Faraday::Adapter::Test::Stubs.new do |stub|
231
+ stub.delete(file_url + '?key=key') { [502, {}, 'Bad Gateway'] }
232
+ end
233
+
234
+ stubbed_connection = Faraday.new do |builder|
235
+ builder.adapter :test, stubs
236
+ end
237
+
238
+ subject.stub(:http_connection).and_return stubbed_connection
239
+
240
+ expect { subject.remove 'WmFxB2aSe20SGT2kzSsr' }.to raise_error InkFilePicker::ServerError
241
+ end
242
+ end
243
+
244
+ context "with secret" do
245
+ it "includes policy and signature" do
246
+ stubs = Faraday::Adapter::Test::Stubs.new do |stub|
247
+ stub.delete(file_url + '?key=key&policy=eyJleHBpcnkiOjEzOTQzNjM4OTYsImNhbGwiOiJyZW1vdmUiLCJoYW5kbGUiOiJXbUZ4QjJhU2UyMFNHVDJrelNzciJ9&signature=a557d55a680892235619ff0bec6c7254fbb8088e53a53d923b4fad1d39df3955') { [200, {}, 'success'] }
248
+ end
249
+
250
+ stubbed_connection = Faraday.new do |builder|
251
+ builder.adapter :test, stubs
252
+ end
253
+
254
+ subject.stub(:http_connection).and_return stubbed_connection
255
+
256
+ response = subject.remove file_url, expiry: 1394363896
257
+
258
+ stubs.verify_stubbed_calls
259
+ expect(response).to be_true
260
+ end
261
+ end
262
+ end
263
+
264
+ describe "#stat" do
265
+ let(:file_url) { 'https://www.filepicker.io/api/file/WmFxB2aSe20SGT2kzSsr' }
266
+
267
+ it "handles server errors correctly" do
268
+ stubs = Faraday::Adapter::Test::Stubs.new do |stub|
269
+ stub.get(file_url + '/metadata') { [502, {}, 'Bad Gateway'] }
270
+ end
271
+
272
+ stubbed_connection = Faraday.new do |builder|
273
+ builder.adapter :test, stubs
274
+ end
275
+
276
+ subject.stub(:http_connection).and_return stubbed_connection
277
+
278
+ expect { subject.stat 'WmFxB2aSe20SGT2kzSsr' }.to raise_error InkFilePicker::ServerError
279
+ end
280
+
281
+ context "with secret" do
282
+ it "includes policy and signature" do
283
+ stubs = Faraday::Adapter::Test::Stubs.new do |stub|
284
+ stub.get(file_url + '/metadata' + '?policy=eyJleHBpcnkiOjEzOTQzNjM4OTYsImNhbGwiOiJzdGF0IiwiaGFuZGxlIjoiV21GeEIyYVNlMjBTR1Qya3pTc3IifQ%3D%3D&signature=d70d11f59750903c628f4e35ecc15ef504d71b1ed104c653fe57b2231a7d667c') do
285
+ [200, {}, '{"mimetype": "image/jpeg", "uploaded": 13.0}']
286
+ end
287
+ end
288
+
289
+ stubbed_connection = Faraday.new do |builder|
290
+ builder.adapter :test, stubs
291
+ end
292
+
293
+ subject.stub(:http_connection).and_return stubbed_connection
294
+
295
+ response = subject.stat file_url, {}, expiry: 1394363896
296
+
297
+ stubs.verify_stubbed_calls
298
+ expect(response['uploaded']).to eq 13.0
299
+ end
300
+
301
+ it "forwards get params" do
302
+ stubs = Faraday::Adapter::Test::Stubs.new do |stub|
303
+ stub.get(file_url + '/metadata' + '?heigth=true&policy=eyJleHBpcnkiOjEzOTQzNjM4OTYsImNhbGwiOiJzdGF0IiwiaGFuZGxlIjoiV21GeEIyYVNlMjBTR1Qya3pTc3IifQ%3D%3D&signature=d70d11f59750903c628f4e35ecc15ef504d71b1ed104c653fe57b2231a7d667c&width=true') do
304
+ [200, {}, '{"width": 100, "height": 100}']
305
+ end
306
+ end
307
+
308
+ stubbed_connection = Faraday.new do |builder|
309
+ builder.adapter :test, stubs
310
+ end
311
+
312
+ subject.stub(:http_connection).and_return stubbed_connection
313
+
314
+ response = subject.stat file_url, {width: true, heigth: true}, expiry: 1394363896
315
+
316
+ stubs.verify_stubbed_calls
317
+ expect(response['width']).to eq 100
318
+ end
319
+ end
320
+ end
321
+
322
+ describe "#convert_url" do
323
+ let(:handle) { 'PHqJHHWpRAGUsIfyx0og' }
324
+ let(:url) { "https://www.filepicker.io/api/file/#{handle}" }
325
+
326
+ context "without secret" do
327
+ before { subject.configuration.secret = nil }
328
+
329
+ it "builds expected convert URL when given a URL" do
330
+ expect(subject.convert_url url, w: 300, h: 200).to eq 'https://www.filepicker.io/api/file/PHqJHHWpRAGUsIfyx0og/convert?h=200&w=300'
331
+ end
332
+
333
+ it "builds expected convert URL when given a handle" do
334
+ expect(subject.convert_url handle, w: 300, h: 200).to eq 'https://www.filepicker.io/api/file/PHqJHHWpRAGUsIfyx0og/convert?h=200&w=300'
335
+ end
336
+ end
337
+
338
+ context "with secret" do
339
+ it "builds expected convert URL when given a URL" do
340
+ expect(subject.convert_url url, {w: 300, h: 200}, expiry: 1394363896).to eq 'https://www.filepicker.io/api/file/PHqJHHWpRAGUsIfyx0og/convert?h=200&policy=eyJleHBpcnkiOjEzOTQzNjM4OTYsImNhbGwiOiJjb252ZXJ0IiwiaGFuZGxlIjoiUEhxSkhIV3BSQUdVc0lmeXgwb2cifQ%3D%3D&signature=b370d4ae604c7917c169fe5b10a6274683bb82056c7b80993a7601d486b89d22&w=300'
341
+ end
342
+ end
343
+ end
344
+
345
+ describe "#retrieve_url" do
346
+ let(:handle) { 'PHqJHHWpRAGUsIfyx0og' }
347
+ let(:url) { "https://www.filepicker.io/api/file/#{handle}" }
348
+
349
+ context "without secret" do
350
+ before { subject.configuration.secret = nil }
351
+
352
+ it "builds expected retrieve URL when given a URL" do
353
+ expect(subject.retrieve_url url).to eq 'https://www.filepicker.io/api/file/PHqJHHWpRAGUsIfyx0og'
354
+ end
355
+
356
+ it "builds expected retrieve URL when given a handle" do
357
+ expect(subject.retrieve_url handle).to eq 'https://www.filepicker.io/api/file/PHqJHHWpRAGUsIfyx0og'
358
+ end
359
+
360
+ it "can include params like cache set to true" do
361
+ expect(subject.retrieve_url handle, cache: true).to eq 'https://www.filepicker.io/api/file/PHqJHHWpRAGUsIfyx0og?cache=true'
362
+ end
363
+ end
364
+
365
+ context "with secret" do
366
+ it "builds expected retrieve URL when given a URL" do
367
+ expect(subject.retrieve_url url, {}, expiry: 1394363896).to eq 'https://www.filepicker.io/api/file/PHqJHHWpRAGUsIfyx0og?policy=eyJleHBpcnkiOjEzOTQzNjM4OTYsImNhbGwiOiJyZWFkIiwiaGFuZGxlIjoiUEhxSkhIV3BSQUdVc0lmeXgwb2cifQ%3D%3D&signature=6bba22df7390a44a13329d2f2ca8317c48317fe6612b21f957670969a074f778'
368
+ end
369
+ end
370
+ end
371
+
372
+
373
+
374
+ describe "#configuration" do
375
+ it "has key set" do
376
+ expect(subject.configuration.key).to eq 'key'
377
+ end
378
+
379
+ it "has secret set" do
380
+ expect(subject.configuration.secret).to eq '6U5CWAU57NAHDC2ICXQKMXYZ4Q'
381
+ end
382
+ end
383
+
384
+ describe "#policy" do
385
+ let(:policy_attributes) { {call: 'read'} }
386
+ let(:policy) { double }
387
+
388
+ describe "expiry" do
389
+ context "is given" do
390
+ it "uses given value" do
391
+ InkFilePicker::Policy.should_receive(:new).with(hash_including(call: 'read', expiry: 60)).and_return policy
392
+
393
+ expect(subject.policy policy_attributes.merge(expiry: 60)).to eq policy
394
+ end
395
+ end
396
+
397
+ context "not given" do
398
+ before { Time.stub_chain(:now, :to_i).and_return 1 }
399
+
400
+ it "uses default_expiry from config" do
401
+ subject.configuration.stub(:default_expiry).and_return 600
402
+
403
+ InkFilePicker::Policy.should_receive(:new).with(hash_including(call: 'read', expiry: 601)).and_return policy
404
+
405
+ expect(subject.policy policy_attributes).to eq policy
406
+ end
407
+ end
408
+ end
409
+ end
410
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ describe InkFilePicker::Configuration do
4
+ let(:attributes) do
5
+ {
6
+ key: 'key',
7
+ secret: 'secret'
8
+ }
9
+ end
10
+
11
+ subject { described_class.new attributes }
12
+
13
+ its(:key) { should eq 'key' }
14
+ its(:secret) { should eq 'secret' }
15
+ its(:default_expiry) { should eq 600 }
16
+ its(:cdn_url) { should eq 'https://www.filepicker.io/api/file/' }
17
+
18
+ describe "#initialize" do
19
+ it "fails when no key is given" do
20
+ expect { described_class.new }.to raise_error ArgumentError
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,28 @@
1
+ require 'spec_helper'
2
+
3
+ describe InkFilePicker::FileHandle do
4
+ let(:cdn_url) { 'https://www.filepicker.io/api/file/' }
5
+ let(:handle) { 'PHqJHHWpRAGUsIfyx0og' }
6
+ let(:url) { "https://www.filepicker.io/api/file/#{handle}" }
7
+
8
+
9
+ describe "#url" do
10
+ it "url passes through if the cdn url is the same as given URL" do
11
+ expect(described_class.new(url, cdn_url).url).to eq url
12
+ end
13
+
14
+ it "builds a file URL given only a file handle" do
15
+ expect(described_class.new(handle, cdn_url).url).to eq url
16
+ end
17
+
18
+ it "ensures that we use CDN" do
19
+ expect(described_class.new(url, 'http://cdn.com/').url).to eq "http://cdn.com/#{handle}"
20
+ end
21
+ end
22
+
23
+ describe "handle" do
24
+ it "returns expected handle from URL" do
25
+ expect(described_class.new(url, cdn_url).handle).to eq handle
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ describe InkFilePicker::Policy do
4
+ let(:secret) { '6U5CWAU57NAHDC2ICXQKMXYZ4Q' }
5
+
6
+ subject do
7
+ described_class.new(
8
+ secret: secret,
9
+ call: 'read',
10
+ expiry: 1394363896
11
+ )
12
+ end
13
+
14
+ its(:policy) { should eq 'eyJleHBpcnkiOjEzOTQzNjM4OTYsImNhbGwiOiJyZWFkIn0=' }
15
+ its(:signature) { should eq '4c50ca71d9e123274a01eb00a7facd52069e07c2e9312517f55bf1b94447792e' }
16
+
17
+ describe "#to_hash" do
18
+ it "contains policy and signature when secret is given" do
19
+ expect(subject.to_hash).to eq({
20
+ policy: subject.policy,
21
+ signature: subject.signature
22
+ })
23
+ end
24
+
25
+ it "returns an empty hash when no secret is given" do
26
+ subject.secret = nil
27
+ expect(subject.to_hash).to eq({})
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+
3
+ describe InkFilePicker do
4
+ describe ".client" do
5
+ it "takes given arguments and initializes a client" do
6
+ client = double
7
+ attributes = {some: 'attributes'}
8
+
9
+ InkFilePicker::Client.should_receive(:new).with(attributes).and_return client
10
+
11
+ expect(described_class.client(attributes)).to eq client
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,6 @@
1
+ require 'bundler/setup'
2
+ require 'ink_file_picker'
3
+
4
+ RSpec.configure do |c|
5
+ c.treat_symbols_as_metadata_keys_with_true_values = true
6
+ end
metadata ADDED
@@ -0,0 +1,150 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ink_file_picker
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Thorbjørn Hermansen
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-03-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: 3.2.14
20
+ - - <
21
+ - !ruby/object:Gem::Version
22
+ version: '5'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 3.2.14
30
+ - - <
31
+ - !ruby/object:Gem::Version
32
+ version: '5'
33
+ - !ruby/object:Gem::Dependency
34
+ name: faraday
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ~>
38
+ - !ruby/object:Gem::Version
39
+ version: 0.9.0
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ~>
45
+ - !ruby/object:Gem::Version
46
+ version: 0.9.0
47
+ - !ruby/object:Gem::Dependency
48
+ name: bundler
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: '1.5'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ~>
59
+ - !ruby/object:Gem::Version
60
+ version: '1.5'
61
+ - !ruby/object:Gem::Dependency
62
+ name: rake
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - '>='
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - '>='
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ - !ruby/object:Gem::Dependency
76
+ name: rspec
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ~>
80
+ - !ruby/object:Gem::Version
81
+ version: 2.14.1
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ~>
87
+ - !ruby/object:Gem::Version
88
+ version: 2.14.1
89
+ description:
90
+ email:
91
+ - thhermansen@gmail.com
92
+ executables: []
93
+ extensions: []
94
+ extra_rdoc_files: []
95
+ files:
96
+ - .gitignore
97
+ - Gemfile
98
+ - LICENSE.txt
99
+ - README.md
100
+ - Rakefile
101
+ - ink_file_picker.gemspec
102
+ - lib/ink_file_picker.rb
103
+ - lib/ink_file_picker/assignable.rb
104
+ - lib/ink_file_picker/client.rb
105
+ - lib/ink_file_picker/configuration.rb
106
+ - lib/ink_file_picker/errors.rb
107
+ - lib/ink_file_picker/file_handle.rb
108
+ - lib/ink_file_picker/policy.rb
109
+ - lib/ink_file_picker/response.rb
110
+ - lib/ink_file_picker/url_builder.rb
111
+ - lib/ink_file_picker/version.rb
112
+ - spec/fixtures/skalar.png
113
+ - spec/ink_file_picker/client_spec.rb
114
+ - spec/ink_file_picker/configuration_spec.rb
115
+ - spec/ink_file_picker/file_handle_spec.rb
116
+ - spec/ink_file_picker/policy_spec.rb
117
+ - spec/ink_file_picker_spec.rb
118
+ - spec/spec_helper.rb
119
+ homepage: ''
120
+ licenses:
121
+ - MIT
122
+ metadata: {}
123
+ post_install_message:
124
+ rdoc_options: []
125
+ require_paths:
126
+ - lib
127
+ required_ruby_version: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - '>='
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ required_rubygems_version: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - '>='
135
+ - !ruby/object:Gem::Version
136
+ version: '0'
137
+ requirements: []
138
+ rubyforge_project:
139
+ rubygems_version: 2.0.0
140
+ signing_key:
141
+ specification_version: 4
142
+ summary: Client for Ink File Picker
143
+ test_files:
144
+ - spec/fixtures/skalar.png
145
+ - spec/ink_file_picker/client_spec.rb
146
+ - spec/ink_file_picker/configuration_spec.rb
147
+ - spec/ink_file_picker/file_handle_spec.rb
148
+ - spec/ink_file_picker/policy_spec.rb
149
+ - spec/ink_file_picker_spec.rb
150
+ - spec/spec_helper.rb