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