murlsh 0.6.1 → 0.7.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 +3 -0
- data/README.textile +25 -2
- data/Rakefile +37 -5
- data/VERSION +1 -1
- data/bin/murlsh +3 -3
- data/config.ru +10 -4
- data/config.yaml +14 -10
- data/lib/murlsh/atom_feed.rb +5 -4
- data/lib/murlsh/auth.rb +6 -7
- data/lib/murlsh/dispatch.rb +25 -23
- data/lib/murlsh/doc.rb +14 -6
- data/lib/murlsh/etag_add_encoding.rb +27 -0
- data/lib/murlsh/flickr_server.rb +54 -0
- data/lib/murlsh/markup.rb +18 -1
- data/lib/murlsh/plugin.rb +2 -0
- data/lib/murlsh/sqlite3_adapter.rb +3 -1
- data/lib/murlsh/time_ago.rb +27 -0
- data/lib/murlsh/uri.rb +3 -1
- data/lib/murlsh/uri_ask.rb +13 -10
- data/lib/murlsh/url.rb +11 -4
- data/lib/murlsh/url_body.rb +21 -31
- data/lib/murlsh/url_server.rb +1 -2
- data/lib/murlsh/yaml_ordered_hash.rb +22 -0
- data/lib/murlsh.rb +4 -18
- data/murlsh.gemspec +22 -5
- data/plugins/add_post_50_update_feed.rb +4 -2
- data/plugins/add_post_50_update_rss.rb +37 -0
- data/plugins/add_post_60_notify_hubs.rb +3 -2
- data/plugins/add_pre_50_lookup_content_type_title.rb +3 -1
- data/plugins/time_50_ago.rb +16 -0
- data/plugins/via_50_domain.rb +36 -0
- data/public/css/screen.css +5 -6
- data/public/js/js.js +142 -94
- data/spec/atom_feed_spec.rb +21 -20
- data/spec/auth_spec.rb +8 -6
- data/spec/dispatch_spec.rb +26 -0
- data/spec/doc_spec.rb +27 -0
- data/spec/markup_spec.rb +3 -1
- data/spec/uri_ask_spec.rb +5 -3
- data/spec/uri_spec.rb +3 -1
- data/spec/url_spec.rb +52 -0
- data/spec/xhtml_response_spec.rb +3 -1
- data/spec/yaml_ordered_hash_spec.rb +28 -0
- metadata +37 -9
- data/lib/murlsh/time.rb +0 -20
data/.gitignore
CHANGED
data/README.textile
CHANGED
@@ -11,9 +11,15 @@ Simple site for a small group of people to share or archive urls.
|
|
11
11
|
* plug-in interface
|
12
12
|
* PubSubHubbub notification
|
13
13
|
|
14
|
+
!http://static.mmb.s3.amazonaws.com/murlsh_screenshot.jpg!
|
15
|
+
|
16
|
+
!http://static.mmb.s3.amazonaws.com/murlsh_iphone_screenshot.jpg!
|
17
|
+
|
14
18
|
See "http://urls.matthewm.boedicker.org/":http://urls.matthewm.boedicker.org/ for example.
|
15
19
|
|
16
|
-
|
20
|
+
h1. Installation
|
21
|
+
|
22
|
+
h2. Phusion Passenger
|
17
23
|
|
18
24
|
<pre>
|
19
25
|
<code>
|
@@ -28,8 +34,25 @@ In the web directory:
|
|
28
34
|
<code>
|
29
35
|
murlsh
|
30
36
|
edit config.yaml
|
31
|
-
rake
|
37
|
+
rake init
|
32
38
|
</code>
|
33
39
|
</pre>
|
34
40
|
|
41
|
+
h1. PubSubHubbub
|
42
|
+
|
43
|
+
Murlsh can notify "PubSubHubbub":http://code.google.com/p/pubsubhubbub/ hubs
|
44
|
+
when a new url is added by adding them to config.yaml. The pubsubhubbub_hubs
|
45
|
+
key is a list of hashes in the following format:
|
46
|
+
|
47
|
+
<pre>
|
48
|
+
<code>
|
49
|
+
pubsubhubbub_hubs:
|
50
|
+
- publish_url: http://pubsubhubbub.appspot.com/publish
|
51
|
+
subscribe_url: http://pubsubhubbub.appspot.com/
|
52
|
+
</code>
|
53
|
+
</pre>
|
54
|
+
|
55
|
+
publish_url is where the notifications get sent
|
56
|
+
subscribe_url is what gets put in the feed as link rel="hub"
|
57
|
+
|
35
58
|
Questions and comments: "matthewm@boedicker.org":mailto:matthewm@boedicker.org
|
data/Rakefile
CHANGED
@@ -8,8 +8,6 @@ pp
|
|
8
8
|
uri
|
9
9
|
yaml
|
10
10
|
|
11
|
-
rubygems
|
12
|
-
|
13
11
|
flog
|
14
12
|
spec/rake/spectask
|
15
13
|
sqlite3
|
@@ -19,6 +17,21 @@ murlsh
|
|
19
17
|
|
20
18
|
config = YAML.load_file('config.yaml')
|
21
19
|
|
20
|
+
desc 'Initialize a new installation.'
|
21
|
+
task :init => %w{db:init user:add compress} do
|
22
|
+
puts <<-eos
|
23
|
+
|
24
|
+
Things you might want to do now:
|
25
|
+
|
26
|
+
- visit #{config['root_url']} in a browser
|
27
|
+
- 'rake post_sh > url_post.sh' to generate a shell script for posting urls
|
28
|
+
|
29
|
+
eos
|
30
|
+
end
|
31
|
+
|
32
|
+
desc 'Combine and compress static files.'
|
33
|
+
task :compress => %w{css:compress js:compress}
|
34
|
+
|
22
35
|
desc "Test remote content type fetch for a URL and show errors."
|
23
36
|
task :content_type, :url do |t, args|
|
24
37
|
puts URI(args.url).extend(Murlsh::UriAsk).content_type(:failproof => false,
|
@@ -96,6 +109,14 @@ end
|
|
96
109
|
desc "Run test suite."
|
97
110
|
Spec::Rake::SpecTask.new('test') do |t|
|
98
111
|
t.spec_files = FileList['spec/*_spec.rb']
|
112
|
+
t.spec_opts = %w{--color}
|
113
|
+
# list of places to check for unicode_formatter.rb and use it if found
|
114
|
+
%w{unicode_formatter.rb}.map { |x| File.expand_path(x) }.each do |f|
|
115
|
+
if File.exists?(f)
|
116
|
+
t.spec_opts.push(*%W{--require #{f} --format UnicodeFormatter})
|
117
|
+
break
|
118
|
+
end
|
119
|
+
end
|
99
120
|
t.verbose = true
|
100
121
|
t.warning = true
|
101
122
|
end
|
@@ -161,7 +182,7 @@ task :post_sh do
|
|
161
182
|
|
162
183
|
URL="$1"
|
163
184
|
VIA="$2"
|
164
|
-
AUTH="$3" # password can be passed as
|
185
|
+
AUTH="$3" # password can be passed as third parameter or hardcoded here
|
165
186
|
|
166
187
|
curl \\
|
167
188
|
--data-urlencode "url=${URL}" \\
|
@@ -189,7 +210,7 @@ namespace :css do
|
|
189
210
|
|
190
211
|
desc 'Combine and compress css.'
|
191
212
|
task :compress => ['public/css'] do
|
192
|
-
combined = cat(config['css_files'].
|
213
|
+
combined = cat(config['css_files'].map { |x| "public/#{x}" }, "\n")
|
193
214
|
|
194
215
|
md5sum = Digest::MD5.hexdigest(combined)
|
195
216
|
|
@@ -204,6 +225,7 @@ namespace :css do
|
|
204
225
|
|
205
226
|
unless config['css_compressed'] == compressed_url
|
206
227
|
config['css_compressed'] = compressed_url
|
228
|
+
config.extend(Murlsh::YamlOrderedHash)
|
207
229
|
open('config.yaml', 'w') { |f| YAML.dump(config, f) }
|
208
230
|
puts "updated config with css_compressed = #{compressed_url}"
|
209
231
|
end
|
@@ -217,7 +239,7 @@ namespace :js do
|
|
217
239
|
|
218
240
|
desc 'Combine and compress javascript.'
|
219
241
|
task :compress => ['public/js'] do
|
220
|
-
combined = cat(config['js_files'].
|
242
|
+
combined = cat(config['js_files'].map { |x| "public/#{x}" } )
|
221
243
|
|
222
244
|
compressed = Net::HTTP.post_form(
|
223
245
|
URI.parse('http://closure-compiler.appspot.com/compile'), {
|
@@ -240,11 +262,20 @@ namespace :js do
|
|
240
262
|
|
241
263
|
unless config['js_compressed'] == compressed_url
|
242
264
|
config['js_compressed'] = compressed_url
|
265
|
+
config.extend(Murlsh::YamlOrderedHash)
|
243
266
|
open('config.yaml', 'w') { |f| YAML.dump(config, f) }
|
244
267
|
puts "updated config with js_compressed = #{compressed_url}"
|
245
268
|
end
|
246
269
|
end
|
247
270
|
|
271
|
+
desc 'Run javascript through jslint.'
|
272
|
+
task :lint do
|
273
|
+
%{public/js/js.js}.each do |jsf|
|
274
|
+
puts jsf
|
275
|
+
puts `rhino http://www.jslint.com/rhino/jslint.js #{jsf}`
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
248
279
|
end
|
249
280
|
|
250
281
|
def ask(prompt, sep=':')
|
@@ -269,6 +300,7 @@ begin
|
|
269
300
|
builder 2.1.2
|
270
301
|
hpricot 0.8.1
|
271
302
|
htmlentities 4.2.0
|
303
|
+
json 1.2.3
|
272
304
|
rack 1.0.0
|
273
305
|
sqlite3-ruby 1.2.1
|
274
306
|
}.each_slice(2) { |g,v| gemspec.add_dependency(g, ">= #{v}") }
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.7.0
|
data/bin/murlsh
CHANGED
@@ -17,7 +17,7 @@ def cp_r_safe(sources, dest, options)
|
|
17
17
|
FileUtils.mkdir_p(new, options)
|
18
18
|
cp_r_safe(Dir.entries(source).
|
19
19
|
reject { |f| %w{. ..}.include?(f) }.
|
20
|
-
|
20
|
+
map { |f| File.join(source, f) }, new, options)
|
21
21
|
else
|
22
22
|
answer = if File.exists?(new)
|
23
23
|
ask("#{new} exists. Overwrite?", 'n')
|
@@ -32,7 +32,7 @@ def cp_r_safe(sources, dest, options)
|
|
32
32
|
end
|
33
33
|
|
34
34
|
cp_r_safe(
|
35
|
-
%w{.htaccess config.ru config.yaml plugins/ public/ Rakefile}.
|
35
|
+
%w{.htaccess config.ru config.yaml plugins/ public/ Rakefile}.map { |x|
|
36
36
|
File.join(File.dirname(__FILE__), '..', x) }, '.', :verbose => true)
|
37
37
|
|
38
38
|
FileUtils.mkdir_p('tmp', :verbose => true)
|
@@ -41,5 +41,5 @@ puts <<eos
|
|
41
41
|
Next steps:
|
42
42
|
|
43
43
|
edit config.yaml
|
44
|
-
rake
|
44
|
+
rake init
|
45
45
|
eos
|
data/config.ru
CHANGED
@@ -1,12 +1,18 @@
|
|
1
1
|
$:.unshift(File.join(File.dirname(__FILE__), 'lib'))
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
%w{
|
4
|
+
yaml
|
5
|
+
|
6
|
+
murlsh
|
7
|
+
}.each { |m| require m }
|
5
8
|
|
6
9
|
# use Rack::ShowExceptions
|
7
10
|
use Rack::ConditionalGet
|
11
|
+
use Murlsh::EtagAddEncoding
|
8
12
|
use Rack::Deflater
|
9
13
|
use Rack::Static, :urls => %w{/css /js /swf}, :root => 'public'
|
10
|
-
use Rack::Static, :urls => %w{/atom.xml}
|
14
|
+
use Rack::Static, :urls => %w{/atom.xml /rss.xml}
|
15
|
+
|
16
|
+
config = YAML.load_file('config.yaml')
|
11
17
|
|
12
|
-
run Murlsh::Dispatch.new
|
18
|
+
run Murlsh::Dispatch.new(config)
|
data/config.yaml
CHANGED
@@ -1,20 +1,24 @@
|
|
1
1
|
---
|
2
2
|
auth_file: murlsh_users
|
3
|
-
|
3
|
+
css_files:
|
4
|
+
- css/jquery.jgrowl.css
|
5
|
+
- css/screen.css
|
4
6
|
db_file: murlsh.db
|
5
7
|
feed_file: atom.xml
|
6
|
-
|
8
|
+
flickr_api_key:
|
7
9
|
gravatar_size: 32
|
8
|
-
|
9
|
-
num_posts_page: 100
|
10
|
-
page_title: mmb url share
|
11
|
-
root_url: http://urls.matthewm.boedicker.org/
|
12
|
-
css_files:
|
13
|
-
- css/jquery.jgrowl.css
|
14
|
-
- css/screen.css
|
15
|
-
js_files:
|
10
|
+
js_files:
|
16
11
|
- js/jquery-1.4.2.min.js
|
17
12
|
- js/jquery.cookie.js
|
18
13
|
- js/jquery.jgrowl_compressed.js
|
19
14
|
- js/js.js
|
15
|
+
meta_tag_description: URLs found interesting by Matthew M. Boedicker
|
16
|
+
meta_tag_verify-v1:
|
17
|
+
meta_tag_viewport: width=device-width,minimum-scale=1.0,maximum-scale=1.0
|
18
|
+
num_posts_feed: 25
|
19
|
+
num_posts_page: 100
|
20
|
+
page_title: mmb url share
|
20
21
|
pubsubhubbub_hubs: []
|
22
|
+
|
23
|
+
root_url: http://urls.matthewm.boedicker.org/
|
24
|
+
show_names: true
|
data/lib/murlsh/atom_feed.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
%w{
|
2
|
+
uri
|
3
3
|
|
4
|
-
|
4
|
+
builder
|
5
|
+
}.each { |m| require m }
|
5
6
|
|
6
7
|
module Murlsh
|
7
8
|
|
@@ -44,7 +45,7 @@ module Murlsh
|
|
44
45
|
xm.link(:href => URI.join(@root_url, @filename), :rel => 'self')
|
45
46
|
@hubs.each { |hub| xm.link(:href => hub, :rel => 'hub') }
|
46
47
|
xm.title(@title)
|
47
|
-
xm.updated(entries.
|
48
|
+
xm.updated(entries.map(&:time).max.xmlschema)
|
48
49
|
entries.each do |mu|
|
49
50
|
xm.entry {
|
50
51
|
xm.author { xm.name(mu.name) }
|
data/lib/murlsh/auth.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
%w{
|
2
|
+
csv
|
3
|
+
digest/md5
|
3
4
|
|
4
|
-
|
5
|
-
require
|
5
|
+
bcrypt
|
6
|
+
}.each { |m| require m }
|
6
7
|
|
7
8
|
module Murlsh
|
8
9
|
|
@@ -16,9 +17,7 @@ module Murlsh
|
|
16
17
|
# See Rakefile for user maintenance tasks.
|
17
18
|
class Auth
|
18
19
|
|
19
|
-
def initialize(file)
|
20
|
-
@file = file
|
21
|
-
end
|
20
|
+
def initialize(file); @file = file; end
|
22
21
|
|
23
22
|
# Authenticate a user by password. Return their name and email if correct.
|
24
23
|
def auth(password)
|
data/lib/murlsh/dispatch.rb
CHANGED
@@ -1,12 +1,8 @@
|
|
1
1
|
%w{
|
2
|
-
murlsh
|
3
|
-
|
4
|
-
rubygems
|
5
2
|
active_record
|
6
3
|
rack
|
7
|
-
sqlite3
|
8
4
|
|
9
|
-
|
5
|
+
murlsh
|
10
6
|
}.each { |m| require m }
|
11
7
|
|
12
8
|
module Murlsh
|
@@ -14,34 +10,40 @@ module Murlsh
|
|
14
10
|
# Dispatch requests.
|
15
11
|
class Dispatch
|
16
12
|
|
17
|
-
# Set up
|
18
|
-
def initialize
|
19
|
-
@config =
|
20
|
-
@url_root = URI(@config.fetch('root_url')).path
|
13
|
+
# Set up database connection and dispatch table.
|
14
|
+
def initialize(config)
|
15
|
+
@config = config
|
21
16
|
|
22
17
|
ActiveRecord::Base.establish_connection(
|
23
18
|
:adapter => 'sqlite3', :database => @config.fetch('db_file'))
|
24
19
|
|
25
|
-
|
20
|
+
db = ActiveRecord::Base.connection.instance_variable_get(:@connection)
|
26
21
|
|
27
|
-
|
22
|
+
url_server = Murlsh::UrlServer.new(@config, db)
|
23
|
+
flickr_server = Murlsh::FlickrServer.new(@config)
|
24
|
+
|
25
|
+
root_path = URI(@config.fetch('root_url')).path
|
26
|
+
|
27
|
+
@dispatch = [
|
28
|
+
[/^GET #{root_path}(url)?$/, url_server.method(:get)],
|
29
|
+
[/^POST #{root_path}(url)?$/, url_server.method(:post)],
|
30
|
+
[/^GET \/flickr$/, flickr_server.method(:get)],
|
31
|
+
]
|
32
|
+
end
|
33
|
+
|
34
|
+
# Figure out which method will handle request.
|
35
|
+
def dispatch(req)
|
36
|
+
method_match = @dispatch.find do |rule|
|
37
|
+
rule[0].match("#{req.request_method} #{req.path}")
|
38
|
+
end
|
39
|
+
|
40
|
+
method_match ? method_match[1] : self.method(:not_found)
|
28
41
|
end
|
29
42
|
|
30
43
|
# Rack call.
|
31
44
|
def call(env)
|
32
|
-
dispatch = {
|
33
|
-
['GET', @url_root] => [@url_server, :get],
|
34
|
-
['POST', @url_root] => [@url_server, :post],
|
35
|
-
['GET', "#{@url_root}url"] => [@url_server, :get],
|
36
|
-
['POST', "#{@url_root}url"] => [@url_server, :post],
|
37
|
-
}
|
38
|
-
dispatch.default = [self, :not_found]
|
39
|
-
|
40
45
|
req = Rack::Request.new(env)
|
41
|
-
|
42
|
-
obj, meth = dispatch[[req.request_method, req.path]]
|
43
|
-
|
44
|
-
obj.send(meth, req).finish
|
46
|
+
dispatch(req).call(req).finish
|
45
47
|
end
|
46
48
|
|
47
49
|
# Called if the request is not found.
|
data/lib/murlsh/doc.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
%w{
|
2
|
+
hpricot
|
3
|
+
}.each { |m| require m }
|
3
4
|
|
4
5
|
module Murlsh
|
5
6
|
|
@@ -21,14 +22,21 @@ module Murlsh
|
|
21
22
|
nil
|
22
23
|
end
|
23
24
|
|
24
|
-
#
|
25
|
-
|
26
|
-
|
27
|
-
|
25
|
+
# Check a list of xpaths in order and return the inner html of the first
|
26
|
+
# one that is not nil.
|
27
|
+
def xpath_search(xpaths)
|
28
|
+
xpaths.each do |xpath|
|
29
|
+
selection = (self/xpath).first
|
30
|
+
return selection.inner_html unless selection.nil?
|
28
31
|
end
|
29
32
|
nil
|
30
33
|
end
|
31
34
|
|
35
|
+
# Get the title of the document.
|
36
|
+
def title
|
37
|
+
xpath_search(%w{//html/head/title //head/title //html/title //title})
|
38
|
+
end
|
39
|
+
|
32
40
|
end
|
33
41
|
|
34
42
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
%w{
|
2
|
+
rack/utils
|
3
|
+
}.each { |m| require m }
|
4
|
+
|
5
|
+
module Murlsh
|
6
|
+
|
7
|
+
# Rack middleware to add the content encoding to the end of the ETag because
|
8
|
+
# ETag must be different for different representations.
|
9
|
+
class EtagAddEncoding
|
10
|
+
|
11
|
+
def initialize(app); @app = app; end
|
12
|
+
|
13
|
+
def call(env)
|
14
|
+
status, headers, body = @app.call(env)
|
15
|
+
|
16
|
+
headers = Rack::Utils::HeaderHash.new(headers)
|
17
|
+
|
18
|
+
if headers['ETag']
|
19
|
+
headers['ETag'].sub!(/(")?$/, "#{headers['Content-Encoding']}\\1")
|
20
|
+
end
|
21
|
+
|
22
|
+
[status, headers, body]
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
%w{
|
2
|
+
open-uri
|
3
|
+
|
4
|
+
json
|
5
|
+
rack
|
6
|
+
}.each { |m| require m }
|
7
|
+
|
8
|
+
module Murlsh
|
9
|
+
|
10
|
+
# Proxy for Flickr rest API flickr.photos.getinfo call to support conditional
|
11
|
+
# get.
|
12
|
+
#
|
13
|
+
# Passes along query string with api key added, returns result from Flickr
|
14
|
+
# with content type set to application/json and last modified header set.
|
15
|
+
class FlickrServer
|
16
|
+
|
17
|
+
def initialize(config); @config = config; end
|
18
|
+
|
19
|
+
# Proxy a flickr.photos.getinfo request to the Flickr rest API.
|
20
|
+
def get(req)
|
21
|
+
resp = Rack::Response.new
|
22
|
+
|
23
|
+
if @config['flickr_api_key']
|
24
|
+
params = req.params.merge('api_key' => @config['flickr_api_key'])
|
25
|
+
|
26
|
+
q = params.map { |k,v| "#{URI.encode(k)}=#{URI.encode(v)}" }.join('&')
|
27
|
+
|
28
|
+
json_wrapped = open("http://api.flickr.com/services/rest/?#{q}") do |f|
|
29
|
+
# for some reason Firefox will not cache if it's text/plain, which is
|
30
|
+
# what Flickr returns
|
31
|
+
resp['Content-Type'] = 'application/json'
|
32
|
+
f.read
|
33
|
+
end
|
34
|
+
|
35
|
+
json = /.+?\((.+)\)/.match(json_wrapped)[1]
|
36
|
+
|
37
|
+
json_parsed = JSON.parse(json)
|
38
|
+
|
39
|
+
resp['Last-Modified'] = Time.at(
|
40
|
+
json_parsed['photo']['dates']['lastupdate'].to_i).httpdate
|
41
|
+
|
42
|
+
resp.body = json_wrapped
|
43
|
+
|
44
|
+
resp
|
45
|
+
else
|
46
|
+
Rack::Response.new('flickr_api_key not set in config.yaml', 500,
|
47
|
+
{ 'Content-Type' => 'text/plain' })
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
data/lib/murlsh/markup.rb
CHANGED
@@ -89,7 +89,24 @@ module Murlsh
|
|
89
89
|
# Query string builder. Takes hash of query string variables.
|
90
90
|
def build_query(h)
|
91
91
|
h.empty? ? '' :
|
92
|
-
'?' + h.
|
92
|
+
'?' + h.map { |k,v| URI.escape("#{k}=#{v}") }.join('&')
|
93
|
+
end
|
94
|
+
|
95
|
+
# Form input builder.
|
96
|
+
def form_input(options)
|
97
|
+
if options[:id]
|
98
|
+
if options[:label]
|
99
|
+
label_suffix = options[:label_suffix] || ':'
|
100
|
+
label("#{options[:label]}#{label_suffix}", :for => options[:id])
|
101
|
+
end
|
102
|
+
options[:name] ||= options[:id]
|
103
|
+
end
|
104
|
+
|
105
|
+
options.delete(:label)
|
106
|
+
|
107
|
+
input({
|
108
|
+
:type => 'text',
|
109
|
+
}.merge(options))
|
93
110
|
end
|
94
111
|
|
95
112
|
private
|
data/lib/murlsh/plugin.rb
CHANGED
@@ -9,6 +9,8 @@ module Murlsh
|
|
9
9
|
# run arguments (config hash)
|
10
10
|
# * hostrec - called to post process the domain that shows after links
|
11
11
|
# run arguments (domain, url, title)
|
12
|
+
# * time - called to convert the time of a post into a string for display
|
13
|
+
# run arguments (time)
|
12
14
|
class Plugin
|
13
15
|
|
14
16
|
# Called when a plugin class inherits from this class (the way plugins
|
@@ -0,0 +1,27 @@
|
|
1
|
+
%w{
|
2
|
+
time
|
3
|
+
}.each { |m| require m }
|
4
|
+
|
5
|
+
module Murlsh
|
6
|
+
|
7
|
+
# Mixin for time class to add fuzzy ago method.
|
8
|
+
module TimeAgo
|
9
|
+
|
10
|
+
# Return a string of the approximate amount of time that has passed since
|
11
|
+
# this time.
|
12
|
+
def ago
|
13
|
+
days_ago = (Time.now.to_i - to_i) / 86400
|
14
|
+
|
15
|
+
case days_ago
|
16
|
+
when 0 then 'today'
|
17
|
+
when 1 then 'yesterday'
|
18
|
+
when (2..4) then "#{days_ago} days ago"
|
19
|
+
when (5..7) then strftime('%a %e %b')
|
20
|
+
when (8..180) then strftime('%e %b').strip
|
21
|
+
else strftime('%e %b %Y').strip
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
data/lib/murlsh/uri.rb
CHANGED
data/lib/murlsh/uri_ask.rb
CHANGED
@@ -1,12 +1,13 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
%w{
|
2
|
+
net/http
|
3
|
+
net/https
|
4
|
+
open-uri
|
5
|
+
uri
|
5
6
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
require
|
7
|
+
hpricot
|
8
|
+
htmlentities
|
9
|
+
iconv
|
10
|
+
}.each { |m| require m }
|
10
11
|
|
11
12
|
module Murlsh
|
12
13
|
|
@@ -52,8 +53,10 @@ module Murlsh
|
|
52
53
|
self.open(options[:headers]) do |f|
|
53
54
|
doc = Hpricot(f).extend(Murlsh::Doc)
|
54
55
|
|
55
|
-
|
56
|
-
|
56
|
+
if doc.title and !doc.title.empty?
|
57
|
+
@title = HTMLEntities.new.decode(Iconv.conv('utf-8',
|
58
|
+
doc.charset || f.charset, doc.title))
|
59
|
+
end
|
57
60
|
end
|
58
61
|
end
|
59
62
|
end
|
data/lib/murlsh/url.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
%w{
|
2
|
+
uri
|
3
3
|
|
4
|
-
|
4
|
+
active_record
|
5
|
+
}.each { |m| require m }
|
5
6
|
|
6
7
|
module Murlsh
|
7
8
|
|
@@ -10,7 +11,13 @@ module Murlsh
|
|
10
11
|
|
11
12
|
# Get the title of this url.
|
12
13
|
def title
|
13
|
-
|
14
|
+
ta = read_attribute(:title)
|
15
|
+
ta = nil if ta and ta.empty?
|
16
|
+
|
17
|
+
ua = read_attribute(:url)
|
18
|
+
ua = nil if ua and ua.empty?
|
19
|
+
|
20
|
+
ta || ua || 'title missing'
|
14
21
|
end
|
15
22
|
|
16
23
|
# Title with whitespace compressed and leading and trailing whitespace
|