athenaeum 1.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -33,7 +33,8 @@ RUBYFORGE_USER = ENV['RUBYFORGE_USER']
33
33
 
34
34
  TEXT_FILES = %w( Rakefile README LICENSE )
35
35
  LIB_FILES = Dir.glob('lib/**/*').delete_if { |item| item.include?( "\.svn" ) }
36
- RELEASE_FILES = TEXT_FILES + LIB_FILES
36
+ BIN_FILES = Dir.glob('bin/*').delete_if { |item| item.include?( "\.svn" ) }
37
+ RELEASE_FILES = TEXT_FILES + LIB_FILES + BIN_FILES
37
38
 
38
39
  task :default => [ :clean ]
39
40
 
@@ -116,7 +117,7 @@ spec = Gem::Specification.new do |s|
116
117
  s.has_rdoc = true
117
118
 
118
119
  s.files = RELEASE_FILES
119
- s.executables = 'athenaeum'
120
+ s.executables = BIN_FILES.map {|file| file.sub( /^bin\//, '' ) }
120
121
 
121
122
  s.autorequire = 'athenaeum'
122
123
  end
@@ -0,0 +1,254 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+
5
+ require 'rubygems'
6
+ require 'athenaeum'
7
+ require 'mongrel'
8
+
9
+ opts = {
10
+ :port => 3030,
11
+ :library => "#{ENV[ 'HOME' ]}/Library/Application\ Support/Delicious\ Library"
12
+ }
13
+
14
+ OptionParser.new do |options|
15
+ options.banner = "Usage: #{File.basename( $0 )} [options]"
16
+
17
+ options.on( "-p PORT", "--port PORT", "Specify port to bind to. Defaults to 3030." ) do |p|
18
+ opts[ :port ] = p
19
+ end
20
+
21
+ options.on( "-l PATH", "--library PATH", "Specify root directory of the Delicious Library support directory. Defaults to ~/Library/Application Support/Delicious Library" ) do |l|
22
+ opts[ :library ] = l
23
+ end
24
+ end.parse!
25
+
26
+ ########################################################################
27
+ ### M O N G R E L H A N D L E R S
28
+ ########################################################################
29
+
30
+ class ItemListHandler < Mongrel::HttpHandler
31
+ def initialize( path )
32
+ @path = path
33
+ end
34
+
35
+ def process( req, resp )
36
+ rescan_library!
37
+
38
+ resp.start do |head,out|
39
+ out << header
40
+
41
+ Athenaeum::Item.types.sort_by {|k| k.type_name}.each do |type|
42
+ items = Athenaeum::Item.find_by_type( type )
43
+ next if items.empty?
44
+
45
+ type_name = type.type_name
46
+ type_name << 's' unless type_name == 'Music'
47
+
48
+ out << "<div class='item_section'>"
49
+ out << "<h1>#{type_name}</h1>"
50
+
51
+ # get the items of this type, sorted by title
52
+ items.sort_by {|item| item.title }.each do |item|
53
+ out << "<div class='item'>"
54
+ out << item.to_html
55
+
56
+ if loan = Athenaeum::Loan.find_by_item_uuid( item.uuid )
57
+ due_date = loan.due_date.strftime( '%B %d, %Y' )
58
+
59
+ out << "<div class='loan'>loaned to #{loan.borrower.to_html}, due on #{due_date}</div>"
60
+ end
61
+
62
+ out << "</div>"
63
+ end
64
+
65
+ out << "<div class='clear'></div>"
66
+ out << "</div>"
67
+ end
68
+
69
+ out << footer
70
+ end
71
+ end
72
+
73
+ # checks to see if we have a current snapshot of the library and makes a new
74
+ # one if not
75
+ def rescan_library!
76
+ path = "#{@path}/Library\ Media\ Data.xml"
77
+ file = File.open( path )
78
+
79
+ @timestamp ||= nil
80
+ @file_size ||= nil
81
+ @md5_hash ||= nil
82
+
83
+ current_hash = `md5 '#{path}'`.split.last
84
+
85
+ file_changed = false
86
+ file_changed = true unless @timestamp == file.stat.ctime and
87
+ @file_size == file.stat.size and
88
+ @md5_hash == current_hash
89
+
90
+ if file_changed
91
+ log "file changed. Rescanning..."
92
+
93
+ Athenaeum::Item.registry.clear
94
+ Athenaeum::Loan.registry.clear
95
+
96
+ doc = Hpricot.XML( file.read )
97
+
98
+ # this query will get us all the top-level books, movies, music, and games
99
+ #from the item container. This prevents us from accidentally grabbing the
100
+ # recommendations and related items.
101
+ doc.at( "/library/items" ).search( "/book | /movie | /music | /game" ) do |item|
102
+ Athenaeum::Item.create( item )
103
+ end
104
+
105
+ # fetches all the borrowers from the xml, creating Athenaeum::Borrower objects
106
+ # for each. This must happen *after* the Items are found, because
107
+ # Athenaeum::Borrower uses Athenaeum::Item's registry to build its internal
108
+ # registry.
109
+ doc.search( "/library/borrowers/borrower" ).each do |b|
110
+ Athenaeum::Borrower.new( b )
111
+ end
112
+
113
+ @timestamp = file.stat.ctime
114
+ @file_size = file.stat.size
115
+ @md5_hash = current_hash
116
+ end
117
+
118
+ file.close
119
+ end
120
+ end
121
+
122
+ class CSSHandler < Mongrel::HttpHandler
123
+ def process( req, resp )
124
+ resp.start do |head,out|
125
+ out << css
126
+ end
127
+ end
128
+ end
129
+
130
+ ########################################################################
131
+ ### T E M P L A T E S
132
+ ########################################################################
133
+
134
+ def css
135
+ return <<-HEREDOC
136
+ body {
137
+ background: #3a3a2b;
138
+ margin: 1em;
139
+
140
+ font-family: sans-serif;
141
+ font-size: 0.74em;
142
+ }
143
+
144
+ a {
145
+ color: #980008;
146
+ text-decoration: none;
147
+ }
148
+
149
+ a:hover { text-decoration: underline; }
150
+
151
+ h1.title, h1.title a, div#footer a { color: #a6d854; }
152
+
153
+ div.item_section {
154
+ border: 1px solid #3a3a2b;
155
+ background: #faf8f1;
156
+
157
+ padding: 0.75em 1em;
158
+
159
+ margin-top: 2em;
160
+ margin-bottom: 1em;
161
+ }
162
+
163
+ div.item_section h1 { margin: 0; }
164
+ span.creator { font-style: italic; }
165
+
166
+ span.title {
167
+ font-size: 1.2em;
168
+ font-weight: bold;
169
+ }
170
+
171
+ div.publish_date {
172
+ padding-top: 2px;
173
+ font-size: 0.8em;
174
+ color: #616253;
175
+ }
176
+
177
+ div.item {
178
+ width: 45%;
179
+ min-height: 4em;
180
+ margin: 1em 2.5% auto 2.5%;
181
+ float:left;
182
+ }
183
+
184
+ div.loan {
185
+ margin-left: 1.5em;
186
+ margin-top: 4px;
187
+ }
188
+
189
+ div.clear { clear: both; }
190
+
191
+ div#footer {
192
+ color: #faf8f1;
193
+ font-size: 0.9em;
194
+ text-align: center;
195
+ line-height: 1.4em;
196
+ }
197
+ HEREDOC
198
+ end
199
+
200
+ def header
201
+ return <<-HEREDOC
202
+ <html>
203
+ <head>
204
+ <title>Delicious Library Contents</title>
205
+ <link rel="stylesheet" type="text/css" href="athenaeum.css">
206
+ </head>
207
+ <body>
208
+ <h1 class="title">Athenaeum &mdash; Your <a href="http://www.delicious-monster.com">Delicious Library</a> on the web!</h1>
209
+ HEREDOC
210
+ end
211
+
212
+ def footer
213
+ return <<-HEREDOC
214
+ <div id="footer">
215
+ <div id="timestamp">updated at #{@timestamp}</div>
216
+ <div id="copyright">Athenaeum is copyright &copy; 2007, <a href="http://www.laika.com">LAIKA, Inc</a>.</div>
217
+ </div>
218
+ </body>
219
+ </html>
220
+ HEREDOC
221
+ end
222
+
223
+ ########################################################################
224
+ ### H E L P E R S
225
+ ########################################################################
226
+
227
+ def log( str )
228
+ puts "[#{Time.now.strftime "%c"}] #{str}"
229
+ end
230
+
231
+ ########################################################################
232
+ ### M O N G R E L I T U P ! ! ! 1
233
+ ########################################################################
234
+
235
+ config = Mongrel::Configurator.new :port => opts[ :port ] do
236
+ listener do
237
+ uri "/", :handler => ItemListHandler.new( opts[ :library ] )
238
+ uri "/athenaeum.css", :handler => CSSHandler.new
239
+ end
240
+
241
+ trap( 'INT' ) {stop}
242
+
243
+ run
244
+ end
245
+
246
+ log "Checking library..."
247
+ unless File.exists? "#{opts[ :library ]}/Library\ Media\ Data.xml"
248
+ log "Library could not be found in #{opts[ :library ]}... exiting."
249
+ config.stop ; exit
250
+ end
251
+
252
+ log "Athenaeum started. Listening on port #{opts[ :port ]}. Reading library in #{opts[ :library ]}."
253
+
254
+ config.join
@@ -17,7 +17,7 @@
17
17
  #
18
18
  # == Version
19
19
  #
20
- # $Id: athenaeum.rb 292 2007-08-02 21:56:18Z bbleything $
20
+ # $Id: athenaeum.rb 299 2007-08-03 22:58:19Z bbleything $
21
21
 
22
22
  ### standard library
23
23
  require 'date'
@@ -33,7 +33,7 @@ module Athenaeum
33
33
  NAME = "Athenaeum"
34
34
  DESCRIPTION = "A small webapp to display the contents of your Delicious Library"
35
35
  LONG_DESC = "Athenaeum is a collection of libraries to read and display the contents of your Delicious Library. It includes the ability to display what items are checked out, to whom, and when they are due."
36
- VERSION = "1.0"
36
+ VERSION = "1.1.0"
37
37
  end
38
38
 
39
39
  ### athenaeum
@@ -17,7 +17,7 @@
17
17
  #
18
18
  # == Version
19
19
  #
20
- # $Id: item.rb 290 2007-08-02 18:26:29Z bbleything $
20
+ # $Id: item.rb 298 2007-08-03 22:57:52Z bbleything $
21
21
 
22
22
  class Athenaeum::Item
23
23
  ########################################################################
@@ -103,7 +103,22 @@ class Athenaeum::Item
103
103
 
104
104
  # returns a decent HTML representation of the item
105
105
  def to_html
106
- return "<span class='title'>#{self.title}</span> <span class='creator'>by #{self.creator}</span>"
106
+ out = []
107
+ out << "<span class='title'>#{self.title}</span>"
108
+ out << "<span class='creator'>by #{self.creator}</span>"
109
+ out << "<br />"
110
+ out << "<div class='publish_date'>Publish Date: #{self.publish_date}</div>"
111
+ return out.join( "\n" )
107
112
  end
108
113
 
114
+ def publish_date
115
+ date = self.published rescue nil
116
+
117
+ if date
118
+ date = Date.strptime( date, '%d-%m-%Y' )
119
+ return date.strftime( '%B %d, %Y' )
120
+ else
121
+ return 'Unknown'
122
+ end
123
+ end
109
124
  end
metadata CHANGED
@@ -3,7 +3,7 @@ rubygems_version: 0.9.4
3
3
  specification_version: 1
4
4
  name: athenaeum
5
5
  version: !ruby/object:Gem::Version
6
- version: "1.0"
6
+ version: 1.1.0
7
7
  date: 2007-09-10 00:00:00 -07:00
8
8
  summary: Athenaeum - A small webapp to display the contents of your Delicious Library
9
9
  require_paths:
@@ -42,6 +42,8 @@ files:
42
42
  - lib/athenaeum/itemtypes/music.rb
43
43
  - lib/athenaeum/loan.rb
44
44
  - lib/athenaeum.rb
45
+ - bin/athenaeum
46
+ - bin/athenaeumd
45
47
  test_files: []
46
48
 
47
49
  rdoc_options: []
@@ -50,6 +52,7 @@ extra_rdoc_files: []
50
52
 
51
53
  executables:
52
54
  - athenaeum
55
+ - athenaeumd
53
56
  extensions: []
54
57
 
55
58
  requirements: []