aviary 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/Gemfile +3 -0
- data/LICENSE +21 -0
- data/README.md +110 -0
- data/Rakefile +10 -0
- data/aviary.gemspec +32 -0
- data/bin/aviary +87 -0
- data/generator/_assets/aviary.css +95 -0
- data/generator/_assets/aviary.js +38 -0
- data/generator/_assets/loader.gif +0 -0
- data/generator/template.erb +45 -0
- data/lib/aviary.rb +28 -0
- data/lib/aviary/configuration.rb +36 -0
- data/lib/aviary/generator.rb +40 -0
- data/lib/aviary/image_host.rb +74 -0
- data/lib/aviary/image_host/flickr.rb +53 -0
- data/lib/aviary/image_host/plixi.rb +27 -0
- data/lib/aviary/image_host/twitpic.rb +13 -0
- data/lib/aviary/image_host/yfrog.rb +13 -0
- data/lib/aviary/page.rb +26 -0
- data/lib/aviary/paginator.rb +54 -0
- data/lib/aviary/search.rb +34 -0
- data/lib/aviary/site.rb +52 -0
- data/lib/aviary/version.rb +3 -0
- data/test/aviary/configuration_test.rb +30 -0
- data/test/aviary/generator_test.rb +33 -0
- data/test/aviary/image_host/flickr_test.rb +60 -0
- data/test/aviary/image_host/plixi_test.rb +30 -0
- data/test/aviary/image_host/twitpic_test.rb +15 -0
- data/test/aviary/image_host/yfrog_test.rb +15 -0
- data/test/aviary/image_host_test.rb +65 -0
- data/test/aviary/page_test.rb +29 -0
- data/test/aviary/paginator_test.rb +48 -0
- data/test/aviary/search_test.rb +43 -0
- data/test/aviary/site_test.rb +66 -0
- data/test/fixtures/flickr.xml +19 -0
- data/test/fixtures/plixi.xml +1 -0
- data/test/fixtures/source/_assets/static +0 -0
- data/test/fixtures/source/_assets/subdir/static +0 -0
- data/test/fixtures/source/template.erb +5 -0
- data/test/fixtures/twitter.json +1 -0
- data/test/helper.rb +9 -0
- metadata +271 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2011 Tate Johnson <tate@tatey.com>
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
# Aviary
|
2
|
+
|
3
|
+
Aviary generates a static photo gallery using Twitter hashtags.
|
4
|
+
|
5
|
+
Twitter is a fantastic resource for discovering photos of events as they unfold. Searching by hastag means you have to do the filtering. Commentary and relinking drown new and interesting photos. In the days the water rose during the 2011 Brisbane floods I wished there was a way to see all the photos without the noise. Now there is.
|
6
|
+
|
7
|
+
See the wiki for a [listing of galleries](https://github.com/tatey/aviary/wiki/galleries).
|
8
|
+
|
9
|
+
## Getting Started
|
10
|
+
|
11
|
+
Install Aviary at the command prompt if you haven't yet
|
12
|
+
|
13
|
+
gem install aviary
|
14
|
+
|
15
|
+
At the command prompt, create a new Aviary template
|
16
|
+
|
17
|
+
aviary new bird
|
18
|
+
|
19
|
+
Change directory and for search tweets tagged with `bird` that have photos
|
20
|
+
|
21
|
+
cd bird/
|
22
|
+
aviary search bird
|
23
|
+
|
24
|
+
Build the static photo gallery
|
25
|
+
|
26
|
+
aviary build
|
27
|
+
|
28
|
+
Preview
|
29
|
+
|
30
|
+
cd _site/
|
31
|
+
gem install asdf
|
32
|
+
asdf .
|
33
|
+
open http://localhost:9292/index.htm
|
34
|
+
|
35
|
+
## Dependencies
|
36
|
+
|
37
|
+
* Ruby 1.8.7, 1.9.2
|
38
|
+
* SQLite3
|
39
|
+
|
40
|
+
### Runtime
|
41
|
+
|
42
|
+
* Base58
|
43
|
+
* DataMapper (Core, SQLite Adapter, Migrations and Validations)
|
44
|
+
* Nokoigir
|
45
|
+
* Twitter
|
46
|
+
|
47
|
+
### Development
|
48
|
+
|
49
|
+
* Bundler
|
50
|
+
* MiniTest
|
51
|
+
* Webmock
|
52
|
+
|
53
|
+
## Customising the Template
|
54
|
+
|
55
|
+
When you create a new aviary you'll notice `_assets/` and `template.erb` are automatically generated for you. Aviary uses these files and directories to generate the static photo gallery.
|
56
|
+
|
57
|
+
Linking back to Aviary is not required, although it is appreciated.
|
58
|
+
|
59
|
+
### template.erb
|
60
|
+
|
61
|
+
Pages are plain ERB templates. You get access to the photos and pagination for the current page. You can control the number of photos per page by using `aviary build --per-page=NUM`.
|
62
|
+
|
63
|
+
`image_hosts` is a collection of photos for the current page.
|
64
|
+
|
65
|
+
<% image_hosts.each do |image_host| %>
|
66
|
+
<a href="<%= image_host.href %>"><img src="<%= image_host.src %>"></a>
|
67
|
+
<p><%= image_host.status.from_user %> said <%= h image_host.status.text %></p>
|
68
|
+
<% end >
|
69
|
+
|
70
|
+
`paginator` is for finding where you are.
|
71
|
+
|
72
|
+
<% if paginator.prev_page? %>
|
73
|
+
<a href="/page<%= paginator.prev_page %>/">Previous</a>
|
74
|
+
<% end %>
|
75
|
+
<% if paginator.next_page? %>
|
76
|
+
<a href="/page<%= paginator.next_page %>/">Next</a>
|
77
|
+
<% end %>
|
78
|
+
|
79
|
+
`h` escapes content which may be unsafe, such as a user's status text.
|
80
|
+
|
81
|
+
<%= h "<script>" %>
|
82
|
+
|
83
|
+
...becomes
|
84
|
+
|
85
|
+
<script>
|
86
|
+
|
87
|
+
### _assets
|
88
|
+
|
89
|
+
Anything inside the `_assets` directory is recursively copied into the root of the destination directory.
|
90
|
+
|
91
|
+
Examples:
|
92
|
+
|
93
|
+
~/bird/_assets/aviary.css -> ~/bird/_site/aviary.css
|
94
|
+
~/bird/_assets/images/status.png -> ~/bird/_site/images/status.png
|
95
|
+
|
96
|
+
Be careful not to name any of your assets with the following names:
|
97
|
+
|
98
|
+
* _assets/index.htm
|
99
|
+
* _assets/page1
|
100
|
+
* _assets/page2
|
101
|
+
* ...
|
102
|
+
* _assets/pageN
|
103
|
+
|
104
|
+
## Why the Name?
|
105
|
+
|
106
|
+
Aviary is defined as "A large cage for keeping birds in". Replace cage with "photo gallery" and birds with "tweets".
|
107
|
+
|
108
|
+
## Copyright
|
109
|
+
|
110
|
+
Copyright © 2010 Tate Johnson. Aviary is released under the MIT license. See LICENSE for details.
|
data/Rakefile
ADDED
data/aviary.gemspec
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/aviary/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = 'aviary'
|
6
|
+
s.version = Aviary::VERSION
|
7
|
+
s.platform = Gem::Platform::RUBY
|
8
|
+
s.authors = ['Tate Johnson']
|
9
|
+
s.email = ['tate@tatey.com']
|
10
|
+
s.homepage = 'https://github.com/tatey/aviary'
|
11
|
+
s.summary = %q{A static photo gallery generator for Twitter}
|
12
|
+
s.description = %q{Aviary generates a static photo gallery using Twitter hashtags}
|
13
|
+
|
14
|
+
s.rubyforge_project = s.name
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ['lib']
|
20
|
+
|
21
|
+
s.add_development_dependency('bundler', '~> 1.0.10')
|
22
|
+
s.add_development_dependency('webmock', '~> 1.6.2')
|
23
|
+
s.add_development_dependency('minitest', '~> 2.0.2')
|
24
|
+
|
25
|
+
s.add_runtime_dependency('base58', '~> 0.1')
|
26
|
+
s.add_runtime_dependency('dm-core', '~> 1.0.2')
|
27
|
+
s.add_runtime_dependency('dm-sqlite-adapter', '~> 1.0.2')
|
28
|
+
s.add_runtime_dependency('dm-migrations', '~> 1.0.2')
|
29
|
+
s.add_runtime_dependency('dm-validations', '~> 1.0.2')
|
30
|
+
s.add_runtime_dependency('nokogiri', '~> 1.4.4')
|
31
|
+
s.add_runtime_dependency('twitter', '~> 1.1.2')
|
32
|
+
end
|
data/bin/aviary
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$: << File.join(File.dirname(__FILE__), '..', 'lib')
|
4
|
+
|
5
|
+
require 'optparse'
|
6
|
+
require 'aviary'
|
7
|
+
|
8
|
+
command = nil
|
9
|
+
options = {}
|
10
|
+
help = <<-HELP
|
11
|
+
Aviary generates a static photo gallery from Twitter hashtags.
|
12
|
+
|
13
|
+
Usage:
|
14
|
+
aviary new HASHTAG # Setup files and directories
|
15
|
+
aviary search HASHTAG [options...] # Search tagged tweets with photos
|
16
|
+
aviary build [options...] # Build gallery
|
17
|
+
|
18
|
+
Help:
|
19
|
+
aviary <command> --help
|
20
|
+
|
21
|
+
HELP
|
22
|
+
|
23
|
+
case ARGV[0]
|
24
|
+
when 'new'
|
25
|
+
if ARGV.size == 2 && ARGV[1] != "--help"
|
26
|
+
options[:source] = File.join(Dir.pwd, ARGV[1])
|
27
|
+
options[:hashtag] = ARGV[1]
|
28
|
+
command = proc { |config| Aviary::Generator.new(config) }
|
29
|
+
else
|
30
|
+
puts "Invalid argument. Try `aviary new HASHTAG`"
|
31
|
+
exit 1
|
32
|
+
end
|
33
|
+
when 'search'
|
34
|
+
OptionParser.new do |o|
|
35
|
+
o.banner = 'Usage: aviary search HASHTAG [options...]'
|
36
|
+
|
37
|
+
o.on('--source DIR', 'Path to source') do |arg|
|
38
|
+
options[:source] = arg
|
39
|
+
end
|
40
|
+
|
41
|
+
o.on('--flickr-api-key KEY', 'Use photos hosted on Flickr') do |arg|
|
42
|
+
options[:flickr_api_key] = arg
|
43
|
+
end
|
44
|
+
|
45
|
+
o.on('--limit NUM', Integer, 'Number of pages to search (100 statuses per page)') do |arg|
|
46
|
+
options[:limit] = arg
|
47
|
+
end
|
48
|
+
|
49
|
+
o.parse!(ARGV)
|
50
|
+
end
|
51
|
+
|
52
|
+
if ARGV.size == 2
|
53
|
+
options[:hashtag] = ARGV[1]
|
54
|
+
command = proc { |config| Aviary::Search.new(config) }
|
55
|
+
else
|
56
|
+
puts "Invalid argument. Run `aviary search --help` for assistance."
|
57
|
+
exit 1
|
58
|
+
end
|
59
|
+
when 'build'
|
60
|
+
OptionParser.new do |o|
|
61
|
+
o.banner = 'Usage: aviary build [options...]'
|
62
|
+
|
63
|
+
o.on('--destination DIR', 'Path to generated gallery') do |arg|
|
64
|
+
options[:dest] = arg
|
65
|
+
end
|
66
|
+
|
67
|
+
o.on('--source DIR', 'Path to source') do |arg|
|
68
|
+
options[:source] = arg
|
69
|
+
end
|
70
|
+
|
71
|
+
o.on('--per-page NUM', Integer, 'Number of photos per page') do |arg|
|
72
|
+
options[:per_page] = arg
|
73
|
+
end
|
74
|
+
|
75
|
+
o.parse!(ARGV)
|
76
|
+
end
|
77
|
+
|
78
|
+
command = proc { |config| Aviary::Site.new(config) }
|
79
|
+
when '--help'
|
80
|
+
puts help
|
81
|
+
exit 0
|
82
|
+
else
|
83
|
+
puts "Invalid command. Run `aviary --help` for assistance."
|
84
|
+
exit 1
|
85
|
+
end
|
86
|
+
|
87
|
+
command.call(Aviary::Configuration.new(:default, options)).process
|
@@ -0,0 +1,95 @@
|
|
1
|
+
html, body {
|
2
|
+
margin: 0;
|
3
|
+
padding: 0;
|
4
|
+
border: 0;
|
5
|
+
background: #222222;
|
6
|
+
font-family: 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;
|
7
|
+
color: #fff;
|
8
|
+
text-align: center;
|
9
|
+
text-shadow: -1px -1px 1px #000;
|
10
|
+
}
|
11
|
+
|
12
|
+
body {
|
13
|
+
width: 750px;
|
14
|
+
margin: 0 auto;
|
15
|
+
}
|
16
|
+
|
17
|
+
h1 {
|
18
|
+
margin-bottom: 0;
|
19
|
+
font-size: 30pt;
|
20
|
+
font-weight: normal;
|
21
|
+
}
|
22
|
+
|
23
|
+
a {
|
24
|
+
color: #fff;
|
25
|
+
text-decoration: none;
|
26
|
+
}
|
27
|
+
|
28
|
+
p a {
|
29
|
+
border-bottom: 1px solid #fff;
|
30
|
+
}
|
31
|
+
|
32
|
+
a.button {
|
33
|
+
display: inline-block;
|
34
|
+
margin: 20px 5px 50px 5px;
|
35
|
+
padding: 10px 20px;
|
36
|
+
background: #333;
|
37
|
+
background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#444), to(#333));
|
38
|
+
background: -moz-linear-gradient(0% 100% 90deg, #333, #444);
|
39
|
+
-webkit-border-radius: 5px;
|
40
|
+
-moz-border-radius: 5px;
|
41
|
+
-webkit-box-shadow: inset 0 1px 0 0 #555;
|
42
|
+
-moz-box-shadow: inset 0 1px 0 0 #555;
|
43
|
+
border: 1px solid #111;
|
44
|
+
font-size: 18pt;
|
45
|
+
text-decoration: none;
|
46
|
+
color: #fff;
|
47
|
+
}
|
48
|
+
|
49
|
+
a.button:active, a.button:hover {
|
50
|
+
background: #444;
|
51
|
+
background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#555), to(#444));
|
52
|
+
background: -moz-linear-gradient(0% 100% 90deg, #444, #555);
|
53
|
+
}
|
54
|
+
|
55
|
+
ul {
|
56
|
+
margin: 0;
|
57
|
+
padding: 0;
|
58
|
+
list-style-type: none;
|
59
|
+
}
|
60
|
+
|
61
|
+
ul li {
|
62
|
+
padding: 35px 0;
|
63
|
+
}
|
64
|
+
|
65
|
+
ul li img.photo {
|
66
|
+
border: none;
|
67
|
+
-webkit-box-shadow: 0 0 25px #111;
|
68
|
+
-moz-box-shadow: 0 0 25px #111;
|
69
|
+
-webkit-transform: scale(1);
|
70
|
+
-webkit-transition-timing-function: ease-out;
|
71
|
+
-webkit-transition-duration: 100ms;
|
72
|
+
-moz-transform: scale(1);
|
73
|
+
-moz-transition-timing-function: ease-out;
|
74
|
+
-moz-transition-duration: 100ms;
|
75
|
+
}
|
76
|
+
|
77
|
+
ul li img.photo:hover {
|
78
|
+
-webkit-transform: scale(1.05);
|
79
|
+
-webkit-transition-timing-function: ease-out;
|
80
|
+
-webkit-transition-duration: 100ms;
|
81
|
+
-moz-transform: scale(1.05);
|
82
|
+
-moz-transition-timing-function: ease-out;
|
83
|
+
-moz-transition-duration: 100ms;
|
84
|
+
}
|
85
|
+
|
86
|
+
blockquote p {
|
87
|
+
width: 500px;
|
88
|
+
margin: 0 auto;
|
89
|
+
color: #aaa;
|
90
|
+
}
|
91
|
+
|
92
|
+
p#author {
|
93
|
+
font-size: 10pt;
|
94
|
+
color: #aaa;
|
95
|
+
}
|
@@ -0,0 +1,38 @@
|
|
1
|
+
(function($) {
|
2
|
+
$(document).ready(function() {
|
3
|
+
// Remove photos that failed to load after 20 seconds.
|
4
|
+
window.setTimeout(function() {
|
5
|
+
$('img.photo:hidden')
|
6
|
+
.closest('li')
|
7
|
+
.fadeOut(function() {
|
8
|
+
$(this)
|
9
|
+
.remove();
|
10
|
+
});
|
11
|
+
}, 20000);
|
12
|
+
|
13
|
+
$('li').each(function(i, el) {
|
14
|
+
var $el = $(el);
|
15
|
+
// Hide contents of each list element.
|
16
|
+
$el
|
17
|
+
.children()
|
18
|
+
.hide();
|
19
|
+
// Prepend the loader.
|
20
|
+
$el
|
21
|
+
.prepend($('<img>', {src: '/loader.gif', className: 'loader'}));
|
22
|
+
// When the photo loads, remove the loader and show the photo.
|
23
|
+
$el
|
24
|
+
.find('img.photo')
|
25
|
+
.load(function() {
|
26
|
+
$el
|
27
|
+
.children('img.loader')
|
28
|
+
.fadeOut(function() {
|
29
|
+
$(this)
|
30
|
+
.remove();
|
31
|
+
$el
|
32
|
+
.children()
|
33
|
+
.fadeIn();
|
34
|
+
});
|
35
|
+
});
|
36
|
+
});
|
37
|
+
});
|
38
|
+
})(jQuery);
|
Binary file
|
@@ -0,0 +1,45 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta name="generator" content="Aviary <%= Aviary::VERSION %> - https://github.com/tatey/aviary">
|
5
|
+
<meta charset="utf-8">
|
6
|
+
<title>#{{hashtag}}</title>
|
7
|
+
<link type="text/css" rel="stylesheet" href="/aviary.css">
|
8
|
+
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.0/jquery.min.js"></script>
|
9
|
+
<script type="text/javascript" src="/aviary.js"></script>
|
10
|
+
</head>
|
11
|
+
<body>
|
12
|
+
<h1><a href="/">#{{hashtag}}</a></h1>
|
13
|
+
<p>
|
14
|
+
A raw collection of photos tweeted with <a href="http://twitter.com/search/%23{{hashtag}}">#{{hashtag}}</a>
|
15
|
+
</p>
|
16
|
+
<ul>
|
17
|
+
<% image_hosts.each do |image_host| %>
|
18
|
+
<li>
|
19
|
+
<a href="<%= image_host.href %>">
|
20
|
+
<img class="photo" src="<%= image_host.src %>">
|
21
|
+
</a>
|
22
|
+
<blockquote>
|
23
|
+
<p>
|
24
|
+
<%= h image_host.status.text %>
|
25
|
+
<cite>
|
26
|
+
<a href="http://twitter.com/<%= image_host.status.from_user %>/status/<%= image_host.status.id %>">
|
27
|
+
@<%= image_host.status.from_user %>
|
28
|
+
</a>
|
29
|
+
</cite>
|
30
|
+
</p>
|
31
|
+
</blockquote>
|
32
|
+
</li>
|
33
|
+
<% end %>
|
34
|
+
</ul>
|
35
|
+
<% if paginator.prev_page? %>
|
36
|
+
<a class="button" id="prev" href="/page<%= paginator.prev_page %>/" title="Previous">◄</a>
|
37
|
+
<% end %>
|
38
|
+
<% if paginator.next_page? %>
|
39
|
+
<a class="button" id="next" href="/page<%= paginator.next_page %>/" title="Next">►</a>
|
40
|
+
<% end %>
|
41
|
+
<p id="author">
|
42
|
+
Generated by <a href="https://github.com/tatey/aviary">Aviary</a>
|
43
|
+
</p>
|
44
|
+
</body>
|
45
|
+
</html>
|