useless-museum 1.2.3

Sign up to get free protection for your applications and to get access to all the features.
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