useless-museum 1.2.3

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: 98f22e2e08471ec4ead397a73c79909897b0874a
4
+ data.tar.gz: 4e0d973edf0d8f32469f21d38fb55952bf4dee5e
5
+ SHA512:
6
+ metadata.gz: 95056d8612069499e3e89d68eea0bdf9a51e0e24940cbe8d2df632467c9fdabe5bcab6d5ea4539ab59072ae355962e5f34cedc3c9d5a4a6f0306788cf56f9190
7
+ data.tar.gz: 59fc975e3558874f1d79497ac36cd2734a6f5d2bd733124cddaa5ca44cbbb71cfa0e9ee30f95ea0663e35725aa4ea376a49502a631b494b07b57dd74e04b5b85
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ Gemfile.lock
2
+ log/*.log
3
+ tmp/
data/.rbenv-version ADDED
@@ -0,0 +1 @@
1
+ 2.0.0-p0
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source "http://rubygems.org"
2
+ gemspec
data/RESEARCH.md ADDED
@@ -0,0 +1,5 @@
1
+ http://en.wikipedia.org/wiki/Museum_studies
2
+ http://en.wikipedia.org/wiki/Cultural_informatics
3
+ http://en.wikipedia.org/wiki/Folksonomy
4
+ http://en.wikipedia.org/wiki/.museum
5
+ http://www.steve.museum/
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require 'bundler/gem_tasks'
@@ -0,0 +1,112 @@
1
+ require 'mini_magick'
2
+ require 'exifr'
3
+
4
+ module Useless
5
+ class Museum
6
+ # Museum::Image is a utility class that provides two types of
7
+ # functionality:
8
+ # * image resizing based upon the Useless::Musuem specifications
9
+ # * access to the following image metadata: latitude, longitude, shot_at
10
+ # It takes an IO object upon initialization. The resize methods return
11
+ # instances of MiniMagick::Image.
12
+ class Image
13
+ def initialize(raw_io)
14
+ @raw_io = raw_io
15
+ end
16
+
17
+ # MiniMagick::Image for the original image
18
+ def base
19
+ @base ||= minimagick_copy
20
+ end
21
+
22
+ # Only JPEGs are considered valid
23
+ def valid?
24
+ base and ['JPEG', 'TIFF', 'PNG', 'GIF'].include?(base['format'])
25
+ end
26
+
27
+ # A 'small' image's longest side is 100 pixels,
28
+ def small
29
+ @small ||= version('100x100')
30
+ end
31
+
32
+ # 'medium' is 500 pixels
33
+ def medium
34
+ @medium ||= version('500x500')
35
+ end
36
+
37
+ # and 'large' is 1024 pixels
38
+ def large
39
+ @large ||= version('1024x1024')
40
+ end
41
+
42
+ # Just grab latitude
43
+ def latitude
44
+ @latitude ||= exifr.gps.latitude if exifr and exifr.gps
45
+ end
46
+
47
+ # and longitude from EXIFR
48
+ def longitude
49
+ @longitude ||= exifr.gps.longitude if exifr and exifr.gps
50
+ end
51
+
52
+ # Shot at is just the date_time provide from EXIFR
53
+ def shot_at
54
+ @shot_at ||= exifr.date_time if exifr
55
+ end
56
+
57
+ private
58
+
59
+ # An EXIFR::JPEG instance, which provides access to the image metadata.
60
+ def exifr
61
+ return nil unless base
62
+
63
+ @exifr ||= case base['format']
64
+ when 'JPEG' then EXIFR::JPEG.new(StringIO.new(blob))
65
+ when 'TIFF' then EXIFR::TIFF.new(StringIO.new(blob))
66
+ else nil
67
+ end
68
+ end
69
+
70
+ def version(dimensions)
71
+ # Copy the image if possible,
72
+ if image = minimagick_copy
73
+
74
+ # resize it to the specified dimensions
75
+ image.resize dimensions
76
+
77
+ # and return
78
+ image
79
+ end
80
+ end
81
+
82
+ def minimagick_copy
83
+ # Just instantiate a new instance with the raw image blob.
84
+ MiniMagick::Image.read(blob)
85
+
86
+ # If the 'image' cannot be parsed,
87
+ rescue MiniMagick::Invalid
88
+
89
+ # just return nil
90
+ nil
91
+ end
92
+
93
+ # A binary blob of the original image
94
+ def blob
95
+ @blob ||= begin
96
+ # Make sure the pointer is up front,
97
+ @raw_io.rewind
98
+
99
+ # read the bytes,
100
+ blob = @raw_io.read
101
+
102
+ # make sure they're un-encoded (encodings can cause multiple bytes to
103
+ # be interpreted as one, which is no good for images),
104
+ blob.force_encoding('BINARY')
105
+
106
+ # and return
107
+ blob
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,5 @@
1
+ module Useless
2
+ class Museum
3
+ VERSION = '1.2.3'
4
+ end
5
+ end
@@ -0,0 +1,158 @@
1
+ require 'sinatra/base'
2
+ require 'json'
3
+
4
+ require 'useless/doc/server/sinatra'
5
+ require 'useless/rack/files'
6
+
7
+ module Useless
8
+ class Museum < Sinatra::Base
9
+ require 'useless/museum/image'
10
+ require 'useless/museum/version'
11
+
12
+ use Useless::Rack::Files
13
+
14
+ register Useless::Doc::Server::Sinatra
15
+
16
+ enable :raise_errors
17
+ disable :show_exceptions, :dump_errors
18
+ set :logging, nil
19
+
20
+ doc 'Museum' do
21
+ url 'http://museum.useless.io'
22
+
23
+ description <<-DESC
24
+ The Museum of Whatever
25
+ DESC
26
+ end
27
+
28
+ get '/' do
29
+ redirect 'http://museum.doc.useless.io'
30
+ end
31
+
32
+ doc.get '/photos/:id' do
33
+ description 'Retrieve information about a photo.'
34
+ authentication_required false
35
+ parameter 'id', 'The ID of the desired photo.', type: 'path'
36
+
37
+ response 404, 'A photo with the specified ID could not be found.'
38
+ response 200, 'The photo was returned successfully.' do
39
+ body do
40
+ content_type 'application/json'
41
+ attribute 'id', 'The ID of the photo.'
42
+ attribute 'file_urls',
43
+ ['An object containing each of files of the photo\'s sizes. "small" has a',
44
+ 'longest side of 100 pixels, "medium" has a longest side of 500 pixels and',
45
+ '"large" has a longest side of 1024 pixels.'].join("\n"), type: 'object'
46
+ attribute 'latitude', 'The latitude where the photo was shot.', type: 'number'
47
+ attribute 'longitude', 'The longitude where the photo was shot.', type: 'number'
48
+ attribute 'shot_at', 'The date and time when the photo was shot.'
49
+ attribute 'description', 'A description of the photo provided by the owner.'
50
+ attribute 'created_by', 'The user who created the photo.', type: 'object'
51
+ attribute 'created_at', 'When the photo was created.'
52
+ end
53
+ end
54
+ end
55
+
56
+ get '/photos/:id' do
57
+ return 404 unless BSON::ObjectId.legal?(params[:id])
58
+
59
+ id = BSON::ObjectId.from_string(params[:id])
60
+ if record = env['useless.mongo']['museum.photos'].find_one(id)
61
+ photo_json(record)
62
+ else
63
+ 404
64
+ end
65
+ end
66
+
67
+ doc.post '/photos' do
68
+ description 'Create a new photo.'
69
+ authentication_required true
70
+
71
+ body do
72
+ content_type 'multipart/form-data'
73
+ attribute 'photo', 'The actual file for the photo.', type: 'file'
74
+ attribute 'latitude',
75
+ ['The latitude where the photo was taken. Only required if it cannot be',
76
+ 'extracted from the photo itself. If the information can be extracted,',
77
+ 'this will be ignored.'].join("\n"),
78
+ required: false, type: 'number', default: 'value extracted from photo'
79
+ attribute 'longitude', 'The longitude where the photo was taken. Follows the same rules as latitude.',
80
+ required: false, type: 'number', default: 'value extracted from photo'
81
+ attribute 'shot_at', 'The time that the photo was taken. Follows the same rules as latitude and longitude.',
82
+ required: false, default: 'value extracted from photo'
83
+ attribute 'description', 'The description of the photo.', required: false, default: nil
84
+ end
85
+
86
+ response 201, 'The photo was created successfully.' do
87
+ header 'Location', 'The URL of the new photo.'
88
+ end
89
+ response 401, 'The request could not be authenticated.'
90
+ response 422, 'latitude, longitude or shot_at could not be extracted from the photo. ' +
91
+ 'Retry with the latitude, longitude and shot_at attributes specified.'
92
+ response 422, 'Invalid image format. Format must be one of JPEG, TIFF, PNG or GIF.'
93
+ end
94
+
95
+ post '/photos' do
96
+ unless env['useless.user']
97
+ halt 401, 'The request could not be authenticated.'
98
+ end
99
+
100
+ unless params[:file]
101
+ halt 422, 'File is a required parameter.'
102
+ end
103
+
104
+ file = params[:file][:tempfile]
105
+ image = Image.new(file)
106
+
107
+ unless image.valid?
108
+ halt 422, 'Invalid image format. Format must be one of JPEG, TIFF, PNG or GIF.'
109
+ end
110
+
111
+ document = {}
112
+ document['latitude'] = image.latitude || params[:latitude]
113
+ document['longitude'] = image.longitude || params[:longitude]
114
+ document['shot_at'] = image.shot_at || (params[:shot_at] && Time.parse(params[:shot_at]))
115
+
116
+ unless document['latitude'] and document['longitude'] and document['shot_at']
117
+ halt 422, 'latitude, longitude or shot_at could not be extracted from the photo. ' +
118
+ 'Retry with the latitude, longitude and shot_at attributes specified.'
119
+ end
120
+
121
+ document['file_url'] = {
122
+ 'small' => create_image_url(image.small),
123
+ 'medium' => create_image_url(image.medium),
124
+ 'large' => create_image_url(image.large)
125
+ }
126
+ document['description'] = params[:description]
127
+ document['created_by_id'] = env['useless.user']['_id']
128
+ document['created_at'] = document['updated_at'] = Time.now
129
+ photo_id = env['useless.mongo']['museum.photos'] << document
130
+
131
+ status 201
132
+ headers 'Location' => "http://museum.useless.io/photos/#{photo_id}"
133
+ content_type 'application/json'
134
+ photo_json(document)
135
+ end
136
+
137
+ def photo_json(raw_record)
138
+ record = raw_record.dup
139
+
140
+ record['id'] = record.delete('_id').to_s
141
+
142
+ record['shot_at'] = raw_record['shot_at'].iso8601
143
+ record['created_at'] = raw_record['created_at'].iso8601
144
+ record['updated_at'] = raw_record['updated_at'].iso8601
145
+
146
+ created_by_id = record.delete('created_by_id')
147
+ created_by = env['useless.mongo']['users'].find_one(created_by_id)
148
+ record['created_by'] = {'id' => created_by_id.to_s, 'handle' => created_by['handle']}
149
+
150
+ record.to_json
151
+ end
152
+
153
+ def create_image_url(image)
154
+ id = env['useless.fs'].put(image.to_blob, content_type: image.mime_type)
155
+ "http://museum.useless.io/files/#{id}"
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,43 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ require 'useless'
4
+ require 'oj'
5
+
6
+ describe 'museum.useless.io Documentation' do
7
+ include Rack::Test::Methods
8
+
9
+ def app
10
+ Useless::Rack.new do
11
+ map 'museum' => Useless::Museum
12
+ end
13
+ end
14
+
15
+ describe 'OPTIONS /' do
16
+ it 'should return JSON for the full API documentation' do
17
+ options 'http://museum.useless.io/'
18
+ last_response.status.should == 200
19
+ doc = Oj.load(last_response.body)
20
+ doc['name'].should == 'Museum'
21
+ doc['url'].should == 'http://museum.useless.io'
22
+ doc['resources'].length.should == 2
23
+ end
24
+ end
25
+
26
+ describe 'OPTIONS /photos/:id' do
27
+ it 'should return JSON for the resource documentation' do
28
+ options 'http://museum.useless.io/photos/:id'
29
+ last_response.status.should == 200
30
+ doc = Oj.load(last_response.body)
31
+ doc['path'].should == '/photos/:id'
32
+ end
33
+ end
34
+
35
+ describe 'OPTIONS /photos' do
36
+ it 'should return JSON for the resource documentation' do
37
+ options 'http://museum.useless.io/photos'
38
+ last_response.status.should == 200
39
+ doc = Oj.load(last_response.body)
40
+ doc['path'].should == '/photos'
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,95 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Useless::Museum::Image do
4
+ def image(path = nil)
5
+ @instance ||= begin
6
+ file = File.open asset_path(path || 'muffin-milk.jpg')
7
+ Useless::Museum::Image.new(file)
8
+ end
9
+ end
10
+
11
+ def assert_image_resize(image, length)
12
+ if image[:width] > image[:height]
13
+ image[:width].should == length
14
+ elsif image[:height] > image[:width]
15
+ image[:height].should == length
16
+ else
17
+ image[:width].should == length and image[:height].should == length
18
+ end
19
+ end
20
+
21
+ describe '#small' do
22
+ it 'should return a MiniMagick::Image object whose longest dimension is 100 pixels' do
23
+ small_image = image.small
24
+ small_image.should be_a(MiniMagick::Image)
25
+ assert_image_resize small_image, 100
26
+ end
27
+ end
28
+
29
+ describe '#medium' do
30
+ it 'should return a MiniMagick::Image object whose longest dimension is 500 pixels' do
31
+ medium_image = image.medium
32
+ medium_image.should be_a(MiniMagick::Image)
33
+ assert_image_resize medium_image, 500
34
+ end
35
+ end
36
+
37
+ describe '#large' do
38
+ it 'should return a MiniMagick::Image object whose longest dimension is 1024 pixels' do
39
+ large_image = image.large
40
+ large_image.should be_a(MiniMagick::Image)
41
+ assert_image_resize large_image, 1024
42
+ end
43
+ end
44
+
45
+ # These specs aren't particularly useful as you have to go on faith that
46
+ # muffin-milk.jpg actually has these values embedded - I guess these can act
47
+ # as a sanity check
48
+ describe '#latitude' do
49
+ it 'should return the latitude value exracted from the image' do
50
+ image.latitude.to_i.should == 40
51
+ end
52
+
53
+ it 'should return nil if the image has no metadata' do
54
+ image('muffin-milk-stripped.jpg').latitude.should be_nil
55
+ end
56
+ end
57
+
58
+ describe '#longitude' do
59
+ it 'should return the latitude value exracted from the image' do
60
+ image.longitude.to_i.should == -73
61
+ end
62
+
63
+ it 'should return nil if the image has no metadata' do
64
+ image('muffin-milk-stripped.jpg').longitude.should be_nil
65
+ end
66
+ end
67
+
68
+ describe '#shot_at' do
69
+ it 'shoulr return the time that the image was taken' do
70
+ image.shot_at.should == Time.parse('2011-11-14T02:23:42Z')
71
+ end
72
+
73
+ it 'should return nil if the image has no metadata' do
74
+ image('muffin-milk-stripped.jpg').shot_at.should be_nil
75
+ end
76
+ end
77
+
78
+ describe '#valid?' do
79
+ it 'should return true if the image is a JPEG' do
80
+ image('muffin-milk.jpg').should be_valid
81
+ end
82
+
83
+ it 'should return true if the image is a PNG' do
84
+ image('muffin-milk.png').should be_valid
85
+ end
86
+
87
+ it 'should return true if the image is a TIFF' do
88
+ image('muffin-milk.tiff').should be_valid
89
+ end
90
+
91
+ it 'should return true if the image is a GIF' do
92
+ image('muffin-milk.gif').should be_valid
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,150 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ require 'useless'
4
+
5
+ describe 'museum.useless.io' do
6
+ include Rack::Test::Methods
7
+
8
+ def app
9
+ Useless::Rack.new do
10
+ map 'museum' => Useless::Museum
11
+ end
12
+ end
13
+
14
+ describe 'GET /photos/:id' do
15
+ it 'should return the record corresponding to the specified ID' do
16
+ shot_at = Time.now - (24 * 60 * 60)
17
+ created_at = Time.now
18
+ user_id = Useless.mongo['users'] << {handle: 'khy'}
19
+ photo_id = Useless.mongo['museum.photos'] << {
20
+ file_url: {
21
+ small: 'http://museum.useless.io/files/abc123',
22
+ medium: 'http://museum.useless.io/files/def456',
23
+ large: 'http://museum.useless.io/files/ghi789'
24
+ },
25
+ latitude: 71.567,
26
+ longitude: 48.987,
27
+ shot_at: shot_at,
28
+ description: 'An image of the Scientology headquarters',
29
+ created_by_id: user_id,
30
+ created_at: created_at,
31
+ updated_at: created_at
32
+ }
33
+
34
+ get "http://museum.useless.io/photos/#{photo_id}"
35
+ last_response.should be_ok
36
+
37
+ body = JSON.parse(last_response.body)
38
+ body['id'].should == photo_id.to_s
39
+ body['file_url']['small'].should == 'http://museum.useless.io/files/abc123'
40
+ body['file_url']['medium'].should == 'http://museum.useless.io/files/def456'
41
+ body['file_url']['large'].should == 'http://museum.useless.io/files/ghi789'
42
+ body['latitude'].should == 71.567
43
+ body['longitude'].should == 48.987
44
+ body['shot_at'].should == shot_at.utc.iso8601
45
+ body['description'].should == 'An image of the Scientology headquarters'
46
+ body['created_by']['id'].should == user_id.to_s
47
+ body['created_by']['handle'].should == 'khy'
48
+ body['created_at'].should == created_at.utc.iso8601
49
+ body['updated_at'].should == created_at.utc.iso8601
50
+ end
51
+
52
+ it 'should return a 404 Not Found if there is no record corresponding to the specified ID' do
53
+ get "http://museum.useless.io/photos/#{BSON::ObjectId.new.to_s}"
54
+ last_response.should be_not_found
55
+ end
56
+
57
+ it 'should return a 404 Not Found if the specified ID is not a valid BSON::ObjectId' do
58
+ get "http://museum.useless.io/photos/invalid-bson-object-id"
59
+ last_response.should be_not_found
60
+ end
61
+ end
62
+
63
+ describe 'POST /photos' do
64
+ it 'should create a new photo record if latitude, longitude and shot_at are not specified,
65
+ but can be extracted from the file and the request is authenticated' do
66
+ user_id = Useless.mongo['users'] << {handle: 'khy', access_token: 'abc123'}
67
+
68
+ header 'Authorization', 'abc123'
69
+ post 'http://museum.useless.io/photos',
70
+ file: upload_file('muffin-milk.jpg', 'image/jpeg'),
71
+ description: 'The Muffin Milk Man!'
72
+
73
+ last_response.status.should == 201
74
+ get last_response.header['Location']
75
+ last_response.should be_ok
76
+
77
+ body = JSON.parse(last_response.body)
78
+ body['latitude'].to_i.should == 40
79
+ body['longitude'].to_i.should == -73
80
+ body['shot_at'].should == '2011-11-14T02:23:42Z'
81
+ body['description'].should == 'The Muffin Milk Man!'
82
+ body['created_by']['id'].should == user_id.to_s
83
+ body['created_by']['handle'].should == 'khy'
84
+ Time.parse(body['created_at']).should be_within(2).of(Time.now)
85
+ Time.parse(body['updated_at']).should be_within(2).of(Time.now)
86
+
87
+ get body['file_url']['small']
88
+ image = MiniMagick::Image.read(last_response.body)
89
+ [image[:width], image[:height]].should include(100)
90
+
91
+ get body['file_url']['medium']
92
+ image = MiniMagick::Image.read(last_response.body)
93
+ [image[:width], image[:height]].should include(500)
94
+
95
+ get body['file_url']['large']
96
+ image = MiniMagick::Image.read(last_response.body)
97
+ [image[:width], image[:height]].should include(1024)
98
+ end
99
+
100
+ it 'should respond with a 401 Unauthorized if no access code is specified' do
101
+ post 'http://museum.useless.io/photos',
102
+ file: upload_file('muffin-milk.jpg', 'image/jpeg'),
103
+ description: 'The Muffin Milk Man!'
104
+ last_response.status.should == 401
105
+ last_response.body.should == 'The request could not be authenticated.'
106
+ end
107
+
108
+ it 'should respond with a 422 Unprocessible Entry if the latitude, longitude and shot at are
109
+ not specified and cannot be extracted' do
110
+ user_id = Useless.mongo['users'] << {handle: 'khy', access_token: 'abc123'}
111
+
112
+ header 'Authorization', 'abc123'
113
+ post 'http://museum.useless.io/photos',
114
+ file: upload_file('muffin-milk-stripped.jpg', 'image/jpeg'),
115
+ description: 'The Muffin Milk Man!'
116
+ last_response.status.should == 422
117
+ last_response.body.should == 'latitude, longitude or shot_at could not be extracted from the photo. Retry with the latitude, longitude and shot_at attributes specified.'
118
+ end
119
+
120
+ it 'should respond with 201 if latitude, longitude and shot_at cannot be extracted but are specified' do
121
+ user_id = Useless.mongo['users'] << {handle: 'khy', access_token: 'abc123'}
122
+
123
+ header 'Authorization', 'abc123'
124
+ post 'http://museum.useless.io/photos',
125
+ file: upload_file('muffin-milk-stripped.jpg', 'image/jpeg'),
126
+ description: 'The Muffin Milk Man!',
127
+ latitude: 71.567,
128
+ longitude: 48.987,
129
+ shot_at: (Time.now - (24 * 60 * 60)).utc.iso8601
130
+
131
+ last_response.status.should == 201
132
+ get last_response.header['Location']
133
+ last_response.should be_ok
134
+ end
135
+
136
+ it 'should respond with a 422 if the file type is invalid, even if latitude, longitude and shot at are specified' do
137
+ user_id = Useless.mongo['users'] << {handle: 'khy', access_token: 'abc123'}
138
+
139
+ header 'Authorization', 'abc123'
140
+ post 'http://museum.useless.io/photos',
141
+ file: upload_file('muffin-milk.txt', 'text/plain'),
142
+ latitude: 71.567,
143
+ longitude: 48.987,
144
+ shot_at: (Time.now - (24 * 60 * 60)).utc.iso8601
145
+
146
+ last_response.status.should == 422
147
+ last_response.body.should == 'Invalid image format. Format must be one of JPEG, TIFF, PNG or GIF.'
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,38 @@
1
+ ENV['RACK_ENV'] = 'test'
2
+ require File.dirname(__FILE__) + '/../lib/useless/museum'
3
+
4
+ require 'rubygems'
5
+ require 'bundler/setup'
6
+
7
+ require 'rack/test'
8
+ require 'useless/rack'
9
+
10
+ RSpec.configure do |config|
11
+ config.order = :rand
12
+
13
+ def clean_database
14
+ Useless.mongo.db.collections.each do |collection|
15
+ if !(collection.name =~ /^system\./)
16
+ collection.drop
17
+ end
18
+ end
19
+ end
20
+
21
+ config.before(:suite){ clean_database }
22
+
23
+ config.after(:each) do
24
+ clean_database
25
+
26
+ # After dropping collections, we need to force a new connection on the
27
+ # next request.
28
+ Useless.mongo.reset_connection!
29
+ end
30
+
31
+ def upload_file(path, content_type = nil)
32
+ Rack::Test::UploadedFile.new(asset_path(path), content_type)
33
+ end
34
+
35
+ def asset_path(path)
36
+ File.dirname(__FILE__) + '/support/assets/' + path
37
+ end
38
+ end
Binary file
Binary file
Binary file
@@ -0,0 +1 @@
1
+ MUFFIN MILK!
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $LOAD_PATH.unshift(File.expand_path('../lib', __FILE__))
3
+ require 'useless/museum'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'useless-museum'
7
+ spec.version = Useless::Museum::VERSION
8
+ spec.authors = ['Kevin Hyland']
9
+ spec.email = ['khy@me.com']
10
+ spec.summary = 'The Museum of Whatever'
11
+ spec.description = 'The Museum of Whatever'
12
+ spec.homepage = 'http://museum.useless.io'
13
+ spec.license = 'MIT'
14
+
15
+ spec.files = `git ls-files`.split("\n")
16
+ spec.test_files = spec.files.grep(%r{^spec/})
17
+ spec.require_paths = ['lib']
18
+
19
+ spec.add_runtime_dependency 'sinatra', '~> 1.4.2'
20
+ spec.add_runtime_dependency 'mini_magick', '~> 3.5.0'
21
+ spec.add_runtime_dependency 'exifr', '~> 1.1.3'
22
+ spec.add_runtime_dependency 'useless', '~> 0.2.0'
23
+ spec.add_runtime_dependency 'useless-doc', '~> 0.6.1'
24
+
25
+ spec.add_development_dependency 'rspec', '~> 2.13.0'
26
+ spec.add_development_dependency 'rack-test', '~> 0.6.2'
27
+ spec.add_development_dependency 'oj', '~> 2.0.10'
28
+ end
metadata ADDED
@@ -0,0 +1,185 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: useless-museum
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.2.3
5
+ platform: ruby
6
+ authors:
7
+ - Kevin Hyland
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-03-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: sinatra
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: 1.4.2
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: 1.4.2
27
+ - !ruby/object:Gem::Dependency
28
+ name: mini_magick
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: 3.5.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: 3.5.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: exifr
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: 1.1.3
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 1.1.3
55
+ - !ruby/object:Gem::Dependency
56
+ name: useless
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 0.2.0
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: 0.2.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: useless-doc
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: 0.6.1
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
81
+ - !ruby/object:Gem::Version
82
+ version: 0.6.1
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ~>
88
+ - !ruby/object:Gem::Version
89
+ version: 2.13.0
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ~>
95
+ - !ruby/object:Gem::Version
96
+ version: 2.13.0
97
+ - !ruby/object:Gem::Dependency
98
+ name: rack-test
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ~>
102
+ - !ruby/object:Gem::Version
103
+ version: 0.6.2
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ~>
109
+ - !ruby/object:Gem::Version
110
+ version: 0.6.2
111
+ - !ruby/object:Gem::Dependency
112
+ name: oj
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ~>
116
+ - !ruby/object:Gem::Version
117
+ version: 2.0.10
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ~>
123
+ - !ruby/object:Gem::Version
124
+ version: 2.0.10
125
+ description: The Museum of Whatever
126
+ email:
127
+ - khy@me.com
128
+ executables: []
129
+ extensions: []
130
+ extra_rdoc_files: []
131
+ files:
132
+ - .gitignore
133
+ - .rbenv-version
134
+ - Gemfile
135
+ - RESEARCH.md
136
+ - Rakefile
137
+ - lib/useless/museum.rb
138
+ - lib/useless/museum/image.rb
139
+ - lib/useless/museum/version.rb
140
+ - spec/documentation_spec.rb
141
+ - spec/museum/image_spec.rb
142
+ - spec/museum_spec.rb
143
+ - spec/spec_helper.rb
144
+ - spec/support/assets/muffin-milk-stripped.jpg
145
+ - spec/support/assets/muffin-milk.gif
146
+ - spec/support/assets/muffin-milk.jpg
147
+ - spec/support/assets/muffin-milk.png
148
+ - spec/support/assets/muffin-milk.tiff
149
+ - spec/support/assets/muffin-milk.txt
150
+ - useless-museum.gemspec
151
+ homepage: http://museum.useless.io
152
+ licenses:
153
+ - MIT
154
+ metadata: {}
155
+ post_install_message:
156
+ rdoc_options: []
157
+ require_paths:
158
+ - lib
159
+ required_ruby_version: !ruby/object:Gem::Requirement
160
+ requirements:
161
+ - - '>='
162
+ - !ruby/object:Gem::Version
163
+ version: '0'
164
+ required_rubygems_version: !ruby/object:Gem::Requirement
165
+ requirements:
166
+ - - '>='
167
+ - !ruby/object:Gem::Version
168
+ version: '0'
169
+ requirements: []
170
+ rubyforge_project:
171
+ rubygems_version: 2.0.0
172
+ signing_key:
173
+ specification_version: 4
174
+ summary: The Museum of Whatever
175
+ test_files:
176
+ - spec/documentation_spec.rb
177
+ - spec/museum/image_spec.rb
178
+ - spec/museum_spec.rb
179
+ - spec/spec_helper.rb
180
+ - spec/support/assets/muffin-milk-stripped.jpg
181
+ - spec/support/assets/muffin-milk.gif
182
+ - spec/support/assets/muffin-milk.jpg
183
+ - spec/support/assets/muffin-milk.png
184
+ - spec/support/assets/muffin-milk.tiff
185
+ - spec/support/assets/muffin-milk.txt