dailyshare 0.1.0
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.
- data/.gitignore +2 -0
- data/Gemfile +3 -0
- data/LICENSE +20 -0
- data/README.md +6 -0
- data/Rakefile +1 -0
- data/dailyshare.gemspec +18 -0
- data/lib/dailyshare/controllers/admin.rb +65 -0
- data/lib/dailyshare/controllers/main.rb +51 -0
- data/lib/dailyshare/helpers/entry.rb +49 -0
- data/lib/dailyshare/helpers/helpers.rb +146 -0
- data/lib/dailyshare/models/member.rb +36 -0
- data/lib/dailyshare/models/photo.rb +87 -0
- data/lib/dailyshare/version.rb +3 -0
- data/lib/dailyshare.rb +39 -0
- metadata +138 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2013 Tyler Kellen
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Dir.glob('tasks/*.rake').each { |r| import r }
|
data/dailyshare.gemspec
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require File.expand_path('../lib/dailyshare/version', __FILE__)
|
2
|
+
|
3
|
+
Gem::Specification.new do |gem|
|
4
|
+
gem.name = 'dailyshare'
|
5
|
+
gem.version = DailyShare::VERSION
|
6
|
+
gem.summary = '356 Projects for Sinatra/Sequel'
|
7
|
+
gem.description = gem.description
|
8
|
+
gem.author = 'Tyler Kellen'
|
9
|
+
gem.email = 'tyler@sleekcode.net'
|
10
|
+
gem.homepage = 'https://github.com/tkellen/ruby-dailyshare'
|
11
|
+
gem.files = `git ls-files`.split("\n")
|
12
|
+
gem.require_paths = ['lib']
|
13
|
+
gem.add_dependency('linguistics')
|
14
|
+
gem.add_dependency('aws-s3')
|
15
|
+
gem.add_dependency('rmagick')
|
16
|
+
gem.add_dependency('exifr')
|
17
|
+
gem.add_dependency('mail')
|
18
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'base64'
|
2
|
+
|
3
|
+
module DailyShare
|
4
|
+
|
5
|
+
class App < Sinatra::Base
|
6
|
+
|
7
|
+
get '/admin/?' do
|
8
|
+
protected!
|
9
|
+
redirect '/'
|
10
|
+
end
|
11
|
+
|
12
|
+
post '/upload/:date_added/:name' do
|
13
|
+
protected!
|
14
|
+
if params[:file]
|
15
|
+
params[:member_id] = Member.byName(params[:name]).id
|
16
|
+
save_photo(params,params[:file][:tempfile])
|
17
|
+
end
|
18
|
+
redirect request.referrer
|
19
|
+
end
|
20
|
+
|
21
|
+
post '/edit/:date/:name' do
|
22
|
+
protected!
|
23
|
+
|
24
|
+
member = Member.byName(params[:name])
|
25
|
+
photo = Photo.by_date_and_member(params[:date],member)
|
26
|
+
photo.update_fields(params,[:title,:description])
|
27
|
+
|
28
|
+
redirect request.referrer
|
29
|
+
end
|
30
|
+
|
31
|
+
##
|
32
|
+
# Receive emailed photos via Postmark.
|
33
|
+
#
|
34
|
+
post '/receiver/?' do
|
35
|
+
# parse post data
|
36
|
+
data = JSON.parse(request.body.read)
|
37
|
+
|
38
|
+
# grab image data
|
39
|
+
image = data['Attachments'][0]['Content']
|
40
|
+
|
41
|
+
# check which member this is based on sender
|
42
|
+
member = Member[:email=>data['From']]
|
43
|
+
|
44
|
+
# was there a member with that email?
|
45
|
+
if !member.nil?
|
46
|
+
|
47
|
+
# get most recent photo
|
48
|
+
recent = Photo.most_recent_by_member(member)
|
49
|
+
|
50
|
+
# only continue if a photo doesn't exist for today
|
51
|
+
if !recent.nil? && recent.date_added+1 <= Date.today
|
52
|
+
|
53
|
+
# prep params for submission
|
54
|
+
params = {
|
55
|
+
:member_id => member.id,
|
56
|
+
:title => data['Subject'],
|
57
|
+
:description => data['TextBody'],
|
58
|
+
:date_added => recent.date_added+1
|
59
|
+
}
|
60
|
+
save_photo(params,StringIO.new(Base64.decode64(image)))
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module DailyShare
|
2
|
+
|
3
|
+
class App < Sinatra::Base
|
4
|
+
|
5
|
+
get '/' do
|
6
|
+
context = context_from_date(Date.today)
|
7
|
+
@title = "#{CONFIG['title']} - day #{context[:count]}"
|
8
|
+
slim :index, :locals => context
|
9
|
+
end
|
10
|
+
|
11
|
+
get '/logout' do
|
12
|
+
session.delete(:member_id)
|
13
|
+
session.delete(:member_name)
|
14
|
+
status 401
|
15
|
+
slim :logout
|
16
|
+
end
|
17
|
+
|
18
|
+
get '/:y/:m/:d?/?' do
|
19
|
+
begin
|
20
|
+
date = date_from_params(params)
|
21
|
+
context = context_from_date(date)
|
22
|
+
@title = "#{CONFIG['title']} - day #{context[:count]}"
|
23
|
+
rescue
|
24
|
+
pass
|
25
|
+
end
|
26
|
+
|
27
|
+
if valid_date?(date)
|
28
|
+
slim :index, :locals => context
|
29
|
+
else
|
30
|
+
redirect request.referrer
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
get '/:name/?' do
|
35
|
+
if (member = Member.byName(params[:name])).nil?
|
36
|
+
redirect request.referrer
|
37
|
+
end
|
38
|
+
|
39
|
+
slim :member, :locals => { :photos => member.photos }
|
40
|
+
end
|
41
|
+
|
42
|
+
get '/:name/missing/?' do
|
43
|
+
if (member = Member.byName(params[:name])).nil?
|
44
|
+
redirect request.referrer
|
45
|
+
end
|
46
|
+
slim :member_missing, :locals => { :photos => member.missingPhotos }
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module DailyShare
|
2
|
+
module Helpers
|
3
|
+
|
4
|
+
def date_from_params(params)
|
5
|
+
Date.parse("#{params[:y]}-#{params[:m]}-#{params[:d]}")
|
6
|
+
end
|
7
|
+
|
8
|
+
def valid_date?(date)
|
9
|
+
(date.year > 2011 || date <= Date.today)
|
10
|
+
end
|
11
|
+
|
12
|
+
def context_from_date(date)
|
13
|
+
{
|
14
|
+
:ymd => date.strftime('%Y-%m-%d'),
|
15
|
+
:date => date,
|
16
|
+
:count => (date-Date.parse('2011-12-31')).to_i.en.numwords,
|
17
|
+
:photos => Member.all_with_photo_for(date)
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
def prev_entry(date)
|
22
|
+
(date-1).strftime('/%Y/%m/%d/')
|
23
|
+
end
|
24
|
+
|
25
|
+
def next_entry(date)
|
26
|
+
(date+1).strftime('/%Y/%m/%d/')
|
27
|
+
end
|
28
|
+
|
29
|
+
def save_photo(params,file)
|
30
|
+
begin
|
31
|
+
photo = Photo.new
|
32
|
+
photo.set_fields(params,[:title,:description,:date_added,:member_id])
|
33
|
+
photo.save_original(file)
|
34
|
+
photo.generate_sizes
|
35
|
+
photo.save_to_s3
|
36
|
+
rescue
|
37
|
+
false
|
38
|
+
else
|
39
|
+
photo.save
|
40
|
+
send_email(
|
41
|
+
CONFIG['email']['from'],
|
42
|
+
"#{CONFIG['title']} - new submisson",
|
43
|
+
slim(:'emails/uploaded', {:locals=>{:photo=>photo},:layout=>false})
|
44
|
+
)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
module DailyShare
|
2
|
+
module Helpers
|
3
|
+
|
4
|
+
##
|
5
|
+
# Generate a cache-busted URL for assets.
|
6
|
+
#
|
7
|
+
# @param [String] url
|
8
|
+
# URL to asset.
|
9
|
+
# @return [String]
|
10
|
+
# URL to asset with cache-busting string added.
|
11
|
+
#
|
12
|
+
def assetPath(url)
|
13
|
+
|
14
|
+
file = File.join(settings.public_folder,'assets',url)
|
15
|
+
if File.exists?(file)
|
16
|
+
cachebust = File.mtime(file).strftime('%s')
|
17
|
+
url = "cb#{cachebust}/#{url}"
|
18
|
+
end
|
19
|
+
|
20
|
+
if ENV['RACK_ENV'] == 'production'
|
21
|
+
CONFIG['url']['cdn']+"/assets/"+url
|
22
|
+
else
|
23
|
+
"/assets/#{url}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
##
|
28
|
+
#
|
29
|
+
# Render a partial template using slim
|
30
|
+
#
|
31
|
+
# @param [String] template
|
32
|
+
# Path to template.
|
33
|
+
# @param [Hash] args
|
34
|
+
# Hash of options for generating a template.
|
35
|
+
# @return [String]
|
36
|
+
# Parsed template html.
|
37
|
+
#
|
38
|
+
def partial(template, *args)
|
39
|
+
template_array = template.to_s.split('/')
|
40
|
+
template = template_array[0..-2].join('/') + "/_#{template_array[-1]}"
|
41
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
42
|
+
options.merge!(:layout => false)
|
43
|
+
locals = options[:locals] || {}
|
44
|
+
if collection = options.delete(:collection) then
|
45
|
+
collection.inject([]) do |buffer, member|
|
46
|
+
buffer << slim(:"#{template}", options.merge(:layout =>
|
47
|
+
false, :locals => { :item => member }.merge(locals)))
|
48
|
+
end.join("\n")
|
49
|
+
else
|
50
|
+
slim(:"#{template}", options)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
##
|
55
|
+
#
|
56
|
+
# Send an multi-part email using mail gem.
|
57
|
+
#
|
58
|
+
# @param [String] to
|
59
|
+
# Address to send email.
|
60
|
+
# @param [String] subject
|
61
|
+
# Subject of email to send
|
62
|
+
# @param [String] body_html
|
63
|
+
# HTML formatted body of email to send
|
64
|
+
# @param [String] from
|
65
|
+
# Address to send email from.
|
66
|
+
# @return [Boolean]
|
67
|
+
# True if mail was delivered, false if not.
|
68
|
+
#
|
69
|
+
def send_email(to,subject,body,from=CONFIG['email_from'])
|
70
|
+
|
71
|
+
# build email
|
72
|
+
mail = Mail.new do
|
73
|
+
|
74
|
+
# assign to/from/subject
|
75
|
+
to to
|
76
|
+
from from
|
77
|
+
subject subject
|
78
|
+
|
79
|
+
# cache html body
|
80
|
+
body_html = body
|
81
|
+
|
82
|
+
# strip html from sent body
|
83
|
+
body_text = body.gsub(/<\/?[^>]*>/,"")
|
84
|
+
|
85
|
+
# send text email by default
|
86
|
+
text_part do
|
87
|
+
body body_text
|
88
|
+
end
|
89
|
+
|
90
|
+
# if body contained html, send that part too
|
91
|
+
if body_html != body_text
|
92
|
+
html_part do
|
93
|
+
content_type 'text/html; charset=UTF-8'
|
94
|
+
body body
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# send it
|
100
|
+
mail.delivery_method :sendmail
|
101
|
+
mail.deliver!
|
102
|
+
end
|
103
|
+
|
104
|
+
def protected!
|
105
|
+
unless authorized?
|
106
|
+
response['WWW-Authenticate'] = %(Basic realm="Restricted Area")
|
107
|
+
throw(:halt, [401, "Not authorized\n"])
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
##
|
112
|
+
#
|
113
|
+
# Find the ordinal suffix for a number.
|
114
|
+
#
|
115
|
+
# @param [Integer] number
|
116
|
+
# A number to be ordinalize
|
117
|
+
# @return [String]
|
118
|
+
# An ordinalized number (e.g. 3 => 3rd)
|
119
|
+
#
|
120
|
+
def ordinalize(number)
|
121
|
+
if (11..13).include?(number.to_i.abs % 100)
|
122
|
+
"#{number}th"
|
123
|
+
else
|
124
|
+
case number.to_i.abs % 10
|
125
|
+
when 1; "#{number}st"
|
126
|
+
when 2; "#{number}nd"
|
127
|
+
when 3; "#{number}rd"
|
128
|
+
else "#{number}th"
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def authorized?
|
134
|
+
return true if !session[:member_id].nil?
|
135
|
+
|
136
|
+
@auth ||= Rack::Auth::Basic::Request.new(request.env)
|
137
|
+
if @auth.provided? && @auth.basic? &&
|
138
|
+
@auth.credentials && @auth.credentials[1] == AUTH['pass'] &&
|
139
|
+
(member = Member.byName(@auth.credentials[0]))
|
140
|
+
session[:member_id] = member.id
|
141
|
+
session[:member_name] = member.name
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
146
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module DailyShare
|
2
|
+
class Member < Sequel::Model
|
3
|
+
one_to_many :photos
|
4
|
+
|
5
|
+
def self.byName(name)
|
6
|
+
self[:name=>name]
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.byEmail(email)
|
10
|
+
self[:email=>email.downcase]
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.all_with_photo_for(date)
|
14
|
+
select(:members__name,:p__title,:p__description,:p__date_added).
|
15
|
+
left_join(:photos.as(:p),[:p__member_id=>:members__id,:date.sql_function(:p__date_added)=>date]).
|
16
|
+
order(:members__id)
|
17
|
+
end
|
18
|
+
|
19
|
+
def missingPhotos
|
20
|
+
DB["SELECT
|
21
|
+
date('2012-01-01'::date+(interval '1 day'*s.num)) AS date
|
22
|
+
FROM
|
23
|
+
(SELECT generate_series(0,now()::date-'2012-01-01'::date) AS num) AS s
|
24
|
+
WHERE
|
25
|
+
(SELECT
|
26
|
+
true
|
27
|
+
FROM
|
28
|
+
photos
|
29
|
+
WHERE
|
30
|
+
date_added=date('2012-01-01'::date+(interval '1 day'*s.num))
|
31
|
+
AND
|
32
|
+
member_id=?) IS null",id]
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require "stringio"
|
2
|
+
|
3
|
+
module DailyShare
|
4
|
+
class Photo < Sequel::Model
|
5
|
+
many_to_one :member
|
6
|
+
|
7
|
+
def self.by_date_and_member(date,member)
|
8
|
+
where(:date.sql_function(:date_added)=>date,:member_id=>member.id).first
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.most_recent_by_member(member)
|
12
|
+
where(:member_id=>member.id).order(:date_added.desc).first
|
13
|
+
end
|
14
|
+
|
15
|
+
def before_save
|
16
|
+
description.strip!
|
17
|
+
end
|
18
|
+
|
19
|
+
def original
|
20
|
+
"#{date_added}-#{self.member.name}.jpg"
|
21
|
+
end
|
22
|
+
|
23
|
+
def thumb
|
24
|
+
"#{date_added}-#{self.member.name}-thumb.jpg"
|
25
|
+
end
|
26
|
+
|
27
|
+
def big
|
28
|
+
"#{date_added}-#{self.member.name}-big.jpg"
|
29
|
+
end
|
30
|
+
|
31
|
+
def save_original(file)
|
32
|
+
image = File.join(CONFIG['photo_dir'],original)
|
33
|
+
File.open(image,'wb') {|f| f.write file.read }
|
34
|
+
image
|
35
|
+
end
|
36
|
+
|
37
|
+
def generate_sizes
|
38
|
+
img = Magick::Image::read(File.join(CONFIG['photo_dir'],original)).first
|
39
|
+
if img
|
40
|
+
img.resize_to_fit(240,240).write(File.join(CONFIG['photo_dir'],thumb))
|
41
|
+
img.resize_to_fit(960,680).write(File.join(CONFIG['photo_dir'],big))
|
42
|
+
img.destroy!
|
43
|
+
true
|
44
|
+
else
|
45
|
+
false
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def sizes
|
50
|
+
[File.join(CONFIG['photo_dir'],original),
|
51
|
+
File.join(CONFIG['photo_dir'],thumb),
|
52
|
+
File.join(CONFIG['photo_dir'],big)]
|
53
|
+
end
|
54
|
+
|
55
|
+
def save_to_s3
|
56
|
+
sizes.each do |file|
|
57
|
+
AWS::S3::S3Object.store(
|
58
|
+
"/photos/#{File.basename(file)}",
|
59
|
+
open(file),
|
60
|
+
AUTH['aws']['bucket'],
|
61
|
+
{
|
62
|
+
:cache_control => 'max-age=315360000',
|
63
|
+
:access => 'public_read'
|
64
|
+
}
|
65
|
+
)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def parse_exif
|
70
|
+
img = EXIFR::JPEG.new(file)
|
71
|
+
|
72
|
+
# store fstop/focal length for comparison
|
73
|
+
fstop = img.exif[:f_number].to_f
|
74
|
+
flen = img.exif[:focal_length].to_f
|
75
|
+
|
76
|
+
{
|
77
|
+
# if no decimal is needed, leave it off
|
78
|
+
:fstop => (fstop == fstop.to_i ? fstop.to_i : fstop),
|
79
|
+
# convert exposure to string
|
80
|
+
:exposure => img.exif[:exposure_time].to_s,
|
81
|
+
# if no decimal is needed, leave it off
|
82
|
+
:focal_length => (flen == flen.to_i ? flen.to_i : flen).to_s+'mm'
|
83
|
+
}
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|
data/lib/dailyshare.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__))
|
2
|
+
|
3
|
+
if !defined?(Sequel) || Sequel::DATABASES.length == 0
|
4
|
+
raise "DailyShare expects a connection to Sequel before being required."
|
5
|
+
end
|
6
|
+
|
7
|
+
if !defined?(DailyShare::App)
|
8
|
+
raise "DailyShare expects a Sinatra app to exist at DailyShare::App."
|
9
|
+
end
|
10
|
+
|
11
|
+
require 'aws/s3'
|
12
|
+
require 'RMagick'
|
13
|
+
require 'exifr'
|
14
|
+
require 'mail'
|
15
|
+
require 'json'
|
16
|
+
|
17
|
+
# prep linguistics gem
|
18
|
+
Encoding.default_external = Encoding::UTF_8
|
19
|
+
Encoding.default_internal = Encoding::UTF_8
|
20
|
+
require 'linguistics'
|
21
|
+
Linguistics::use(:en)
|
22
|
+
|
23
|
+
# load helpers
|
24
|
+
require 'dailyshare/helpers/entry'
|
25
|
+
require 'dailyshare/helpers/helpers'
|
26
|
+
|
27
|
+
# load models
|
28
|
+
require 'dailyshare/models/member'
|
29
|
+
require 'dailyshare/models/photo'
|
30
|
+
|
31
|
+
# load controllers
|
32
|
+
require 'dailyshare/controllers/admin'
|
33
|
+
require 'dailyshare/controllers/main'
|
34
|
+
|
35
|
+
module DailyShare
|
36
|
+
class App < Sinatra::Base
|
37
|
+
helpers DailyShare::Helpers
|
38
|
+
end
|
39
|
+
end
|
metadata
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: dailyshare
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Tyler Kellen
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-12-29 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: linguistics
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: aws-s3
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rmagick
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: exifr
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :runtime
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: mail
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :runtime
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
description: ''
|
95
|
+
email: tyler@sleekcode.net
|
96
|
+
executables: []
|
97
|
+
extensions: []
|
98
|
+
extra_rdoc_files: []
|
99
|
+
files:
|
100
|
+
- .gitignore
|
101
|
+
- Gemfile
|
102
|
+
- LICENSE
|
103
|
+
- README.md
|
104
|
+
- Rakefile
|
105
|
+
- dailyshare.gemspec
|
106
|
+
- lib/dailyshare.rb
|
107
|
+
- lib/dailyshare/controllers/admin.rb
|
108
|
+
- lib/dailyshare/controllers/main.rb
|
109
|
+
- lib/dailyshare/helpers/entry.rb
|
110
|
+
- lib/dailyshare/helpers/helpers.rb
|
111
|
+
- lib/dailyshare/models/member.rb
|
112
|
+
- lib/dailyshare/models/photo.rb
|
113
|
+
- lib/dailyshare/version.rb
|
114
|
+
homepage: https://github.com/tkellen/ruby-dailyshare
|
115
|
+
licenses: []
|
116
|
+
post_install_message:
|
117
|
+
rdoc_options: []
|
118
|
+
require_paths:
|
119
|
+
- lib
|
120
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
127
|
+
none: false
|
128
|
+
requirements:
|
129
|
+
- - ! '>='
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
requirements: []
|
133
|
+
rubyforge_project:
|
134
|
+
rubygems_version: 1.8.24
|
135
|
+
signing_key:
|
136
|
+
specification_version: 3
|
137
|
+
summary: 356 Projects for Sinatra/Sequel
|
138
|
+
test_files: []
|