murlsh 0.7.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +1 -0
- data/VERSION +1 -1
- data/config.ru +14 -1
- data/config.yaml +0 -1
- data/lib/murlsh/dispatch.rb +5 -3
- data/lib/murlsh/doc.rb +15 -3
- data/lib/murlsh/far_future_expires.rb +37 -0
- data/lib/murlsh/flickr_server.rb +7 -7
- data/lib/murlsh/markup.rb +2 -2
- data/lib/murlsh/must_revalidate.rb +36 -0
- data/lib/murlsh/twitter_server.rb +44 -0
- data/lib/murlsh/uri_ask.rb +52 -12
- data/lib/murlsh/url_server.rb +2 -6
- data/murlsh.gemspec +9 -3
- data/plugins/add_pre_60_github_title.rb +22 -0
- data/public/js/js.js +10 -7
- data/spec/doc_spec.rb +29 -4
- data/spec/yaml_ordered_hash_spec.rb +5 -4
- metadata +23 -6
- data/public/js/jquery.cookie.js +0 -96
data/Rakefile
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.8.0
|
data/config.ru
CHANGED
@@ -3,16 +3,29 @@ $:.unshift(File.join(File.dirname(__FILE__), 'lib'))
|
|
3
3
|
%w{
|
4
4
|
yaml
|
5
5
|
|
6
|
+
rack/cache
|
7
|
+
|
6
8
|
murlsh
|
7
9
|
}.each { |m| require m }
|
8
10
|
|
11
|
+
config = YAML.load_file('config.yaml')
|
12
|
+
|
9
13
|
# use Rack::ShowExceptions
|
14
|
+
use Rack::Cache,
|
15
|
+
:verbose => true,
|
16
|
+
:metastore => 'file:tmp/cache/rack/meta',
|
17
|
+
:entitystore => 'file:tmp/cache/rack/body'
|
10
18
|
use Rack::ConditionalGet
|
11
19
|
use Murlsh::EtagAddEncoding
|
12
20
|
use Rack::Deflater
|
21
|
+
use Murlsh::FarFutureExpires, :patterns => %r{\.gen\.(css|js)$}
|
22
|
+
|
23
|
+
feed_path = URI.join(config.fetch('root_url'), config.fetch('feed_file')).path
|
24
|
+
use Murlsh::MustRevalidate, :patterns => %r{^#{Regexp.escape(feed_path)}$}
|
25
|
+
|
13
26
|
use Rack::Static, :urls => %w{/css /js /swf}, :root => 'public'
|
14
27
|
use Rack::Static, :urls => %w{/atom.xml /rss.xml}
|
15
28
|
|
16
|
-
|
29
|
+
# use Rack::Lint
|
17
30
|
|
18
31
|
run Murlsh::Dispatch.new(config)
|
data/config.yaml
CHANGED
data/lib/murlsh/dispatch.rb
CHANGED
@@ -21,13 +21,15 @@ module Murlsh
|
|
21
21
|
|
22
22
|
url_server = Murlsh::UrlServer.new(@config, db)
|
23
23
|
flickr_server = Murlsh::FlickrServer.new(@config)
|
24
|
+
twitter_server = Murlsh::TwitterServer.new
|
24
25
|
|
25
26
|
root_path = URI(@config.fetch('root_url')).path
|
26
27
|
|
27
28
|
@dispatch = [
|
28
|
-
[
|
29
|
-
[
|
30
|
-
[
|
29
|
+
[%r{^GET #{root_path}(url)?$}, url_server.method(:get)],
|
30
|
+
[%r{^POST #{root_path}(url)?$}, url_server.method(:post)],
|
31
|
+
[%r{^GET /flickr$}, flickr_server.method(:get)],
|
32
|
+
[%r{^GET /twitter/.+$}, twitter_server.method(:get)],
|
31
33
|
]
|
32
34
|
end
|
33
35
|
|
data/lib/murlsh/doc.rb
CHANGED
@@ -25,16 +25,28 @@ module Murlsh
|
|
25
25
|
# Check a list of xpaths in order and return the inner html of the first
|
26
26
|
# one that is not nil.
|
27
27
|
def xpath_search(xpaths)
|
28
|
-
xpaths.each do |xpath|
|
28
|
+
[*xpaths].each do |xpath|
|
29
29
|
selection = (self/xpath).first
|
30
|
-
|
30
|
+
if selection; return (yield selection); end
|
31
31
|
end
|
32
32
|
nil
|
33
33
|
end
|
34
34
|
|
35
35
|
# Get the title of the document.
|
36
36
|
def title
|
37
|
-
xpath_search(%w{
|
37
|
+
xpath_search(%w{
|
38
|
+
//html/head/title
|
39
|
+
//head/title
|
40
|
+
//html/title
|
41
|
+
//title
|
42
|
+
}) { |node| node.inner_html }
|
43
|
+
end
|
44
|
+
|
45
|
+
# Get the meta description of the document.
|
46
|
+
def description
|
47
|
+
xpath_search(
|
48
|
+
"//html/head/meta[@name='description']"
|
49
|
+
) { |node| node['content'] }
|
38
50
|
end
|
39
51
|
|
40
52
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
%w{
|
2
|
+
rack
|
3
|
+
rack/utils
|
4
|
+
}.each { |m| require m }
|
5
|
+
|
6
|
+
module Murlsh
|
7
|
+
|
8
|
+
# Rack middleware to set a far future expires header for urls that match
|
9
|
+
# patterns.
|
10
|
+
class FarFutureExpires
|
11
|
+
|
12
|
+
def initialize(app, options={})
|
13
|
+
@app = app
|
14
|
+
@patterns = options[:patterns] ? [*options[:patterns]] : []
|
15
|
+
@future = options[:future] || 'Wed, 22 Jun 2019 20:07:00 GMT'
|
16
|
+
end
|
17
|
+
|
18
|
+
def call(env)
|
19
|
+
status, headers, body = @app.call(env)
|
20
|
+
|
21
|
+
req = Rack::Request.new(env)
|
22
|
+
|
23
|
+
headers = Rack::Utils::HeaderHash.new(headers)
|
24
|
+
|
25
|
+
@patterns.each do |pattern|
|
26
|
+
if pattern.match(req.path)
|
27
|
+
headers['Expires'] = @future
|
28
|
+
break
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
[status, headers, body]
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
data/lib/murlsh/flickr_server.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
%w{
|
2
|
+
digest/sha1
|
2
3
|
open-uri
|
3
4
|
|
4
5
|
json
|
@@ -7,16 +8,15 @@ rack
|
|
7
8
|
|
8
9
|
module Murlsh
|
9
10
|
|
10
|
-
# Proxy for Flickr rest API
|
11
|
-
# get.
|
11
|
+
# Proxy for Flickr rest API to support conditional get and caching.
|
12
12
|
#
|
13
13
|
# Passes along query string with api key added, returns result from Flickr
|
14
|
-
# with
|
14
|
+
# with cache-control, etag and last-modified headers set.
|
15
15
|
class FlickrServer
|
16
16
|
|
17
17
|
def initialize(config); @config = config; end
|
18
18
|
|
19
|
-
# Proxy a
|
19
|
+
# Proxy a request to the Flickr API.
|
20
20
|
def get(req)
|
21
21
|
resp = Rack::Response.new
|
22
22
|
|
@@ -26,9 +26,6 @@ module Murlsh
|
|
26
26
|
q = params.map { |k,v| "#{URI.encode(k)}=#{URI.encode(v)}" }.join('&')
|
27
27
|
|
28
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
29
|
f.read
|
33
30
|
end
|
34
31
|
|
@@ -36,6 +33,9 @@ module Murlsh
|
|
36
33
|
|
37
34
|
json_parsed = JSON.parse(json)
|
38
35
|
|
36
|
+
resp['Cache-Control'] = 'max-age=86400'
|
37
|
+
resp['Content-Type'] = 'application/json'
|
38
|
+
resp['ETag'] = "\"#{Digest::SHA1.hexdigest(json_wrapped)}\""
|
39
39
|
resp['Last-Modified'] = Time.at(
|
40
40
|
json_parsed['photo']['dates']['lastupdate'].to_i).httpdate
|
41
41
|
|
data/lib/murlsh/markup.rb
CHANGED
@@ -8,7 +8,7 @@ module Murlsh
|
|
8
8
|
# Options:
|
9
9
|
# * :prefix - prefix to append to all script urls
|
10
10
|
def javascript(sources, options={})
|
11
|
-
sources.
|
11
|
+
[*sources].each do |src|
|
12
12
|
script('', :type => 'text/javascript',
|
13
13
|
:src => "#{options[:prefix]}#{src}")
|
14
14
|
end
|
@@ -49,7 +49,7 @@ module Murlsh
|
|
49
49
|
# * :media - optional media attribute
|
50
50
|
# * :prefix - prepended to all CSS urls
|
51
51
|
def css(hrefs, options={})
|
52
|
-
hrefs.
|
52
|
+
[*hrefs].each do |href|
|
53
53
|
attrs = {
|
54
54
|
:href => "#{options[:prefix]}#{href}",
|
55
55
|
:rel => 'stylesheet',
|
@@ -0,0 +1,36 @@
|
|
1
|
+
%w{
|
2
|
+
rack
|
3
|
+
rack/utils
|
4
|
+
}.each { |m| require m }
|
5
|
+
|
6
|
+
module Murlsh
|
7
|
+
|
8
|
+
# Rack middleware to force caches to always revalidate for urls that match
|
9
|
+
# patterns.
|
10
|
+
class MustRevalidate
|
11
|
+
|
12
|
+
def initialize(app, options={})
|
13
|
+
@app = app
|
14
|
+
@patterns = options[:patterns] ? [*options[:patterns]] : []
|
15
|
+
end
|
16
|
+
|
17
|
+
def call(env)
|
18
|
+
status, headers, body = @app.call(env)
|
19
|
+
|
20
|
+
req = Rack::Request.new(env)
|
21
|
+
|
22
|
+
headers = Rack::Utils::HeaderHash.new(headers)
|
23
|
+
|
24
|
+
@patterns.each do |pattern|
|
25
|
+
if pattern.match(req.path)
|
26
|
+
headers['Cache-Control'] = 'must-revalidate, max-age=0'
|
27
|
+
break
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
[status, headers, body]
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
%w{
|
2
|
+
digest/sha1
|
3
|
+
open-uri
|
4
|
+
|
5
|
+
json
|
6
|
+
rack
|
7
|
+
}.each { |m| require m }
|
8
|
+
|
9
|
+
module Murlsh
|
10
|
+
|
11
|
+
# Proxy for Twitter rest API to support conditional get and caching.
|
12
|
+
#
|
13
|
+
# Passes along path and query string, returns result from Twitter with
|
14
|
+
# cache-control, etag and last-modified headers set.
|
15
|
+
class TwitterServer
|
16
|
+
|
17
|
+
# Proxy a request to the Twitter API.
|
18
|
+
def get(req)
|
19
|
+
resp = Rack::Response.new
|
20
|
+
|
21
|
+
twitter_url = URI.join('http://api.twitter.com',
|
22
|
+
req.fullpath[/twitter\/(.+)/, 1])
|
23
|
+
|
24
|
+
json_wrapped = open(twitter_url) do |f|
|
25
|
+
resp['Content-Type'] = f.content_type
|
26
|
+
f.read
|
27
|
+
end
|
28
|
+
|
29
|
+
json = /.+?\((.+)\)/.match(json_wrapped)[1]
|
30
|
+
|
31
|
+
json_parsed = JSON.parse(json)
|
32
|
+
|
33
|
+
resp['Cache-Control'] = 'max-age=86400'
|
34
|
+
resp['ETag'] = "\"#{Digest::SHA1.hexdigest(json_wrapped)}\""
|
35
|
+
resp['Last-Modified'] = Time.parse(json_parsed['created_at']).httpdate
|
36
|
+
|
37
|
+
resp.body = json_wrapped
|
38
|
+
|
39
|
+
resp
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
data/lib/murlsh/uri_ask.rb
CHANGED
@@ -41,26 +41,61 @@ module Murlsh
|
|
41
41
|
# Get the HTML title.
|
42
42
|
#
|
43
43
|
# Options:
|
44
|
-
# * :failproof - if true hide all exceptions and return
|
44
|
+
# * :failproof - if true hide all exceptions and return url on failure
|
45
45
|
# * :headers - hash of headers to send in request
|
46
46
|
def title(options={})
|
47
47
|
return @title if defined?(@title)
|
48
|
-
options[:headers] = default_headers.merge(options.fetch(:headers, {}))
|
49
48
|
|
50
49
|
@title = to_s
|
51
|
-
|
50
|
+
|
51
|
+
d = doc(options)
|
52
|
+
|
53
|
+
if d and d.title and !d.title.empty?
|
54
|
+
@title = decode(d.title)
|
55
|
+
end
|
56
|
+
|
57
|
+
@title
|
58
|
+
end
|
59
|
+
|
60
|
+
# Get the HTML meta description.
|
61
|
+
#
|
62
|
+
# Options:
|
63
|
+
# * :failproof - if true hide all exceptions and return empty string on failure
|
64
|
+
# * :headers - hash of headers to send in request
|
65
|
+
def description(options={})
|
66
|
+
return @description if defined?(@description)
|
67
|
+
|
68
|
+
@description = ''
|
69
|
+
|
70
|
+
d = doc(options)
|
71
|
+
|
72
|
+
if d and d.description and !d.description.empty?
|
73
|
+
@description = decode(d.description)
|
74
|
+
end
|
75
|
+
|
76
|
+
@description
|
77
|
+
end
|
78
|
+
|
79
|
+
# Get the parsed Hpricot doc at this url.
|
80
|
+
#
|
81
|
+
# Options:
|
82
|
+
# * :failproof - if true hide all exceptions and return empty string on failure
|
83
|
+
# * :headers - hash of headers to send in request
|
84
|
+
def doc(options={})
|
85
|
+
return @doc if defined?(@doc)
|
86
|
+
options[:headers] = default_headers.merge(options.fetch(:headers, {}))
|
87
|
+
|
88
|
+
@doc = nil
|
89
|
+
if html?(options)
|
52
90
|
Murlsh::failproof(options) do
|
53
91
|
self.open(options[:headers]) do |f|
|
54
|
-
doc = Hpricot(f).extend(Murlsh::Doc)
|
92
|
+
@doc = Hpricot(f).extend(Murlsh::Doc)
|
55
93
|
|
56
|
-
|
57
|
-
@title = HTMLEntities.new.decode(Iconv.conv('utf-8',
|
58
|
-
doc.charset || f.charset, doc.title))
|
59
|
-
end
|
94
|
+
@charset = @doc.charset || f.charset
|
60
95
|
end
|
61
96
|
end
|
62
97
|
end
|
63
|
-
@
|
98
|
+
@doc
|
64
99
|
end
|
65
100
|
|
66
101
|
# Default headers sent with the request.
|
@@ -76,12 +111,17 @@ module Murlsh
|
|
76
111
|
result
|
77
112
|
end
|
78
113
|
|
79
|
-
# Return true if the content type is
|
80
|
-
|
81
|
-
def might_have_title?(options={})
|
114
|
+
# Return true if the content type is HTML.
|
115
|
+
def html?(options={})
|
82
116
|
content_type(options)[/^text\/html/]
|
83
117
|
end
|
84
118
|
|
119
|
+
# Convert from the character set of this url to utf-8 and decode HTML
|
120
|
+
# entities.
|
121
|
+
def decode(s)
|
122
|
+
HTMLEntities.new.decode(Iconv.conv('utf-8', @charset, s))
|
123
|
+
end
|
124
|
+
|
85
125
|
end
|
86
126
|
|
87
127
|
end
|
data/lib/murlsh/url_server.rb
CHANGED
@@ -23,8 +23,9 @@ module Murlsh
|
|
23
23
|
resp.set_content_type(req.env['HTTP_ACCEPT'], req.env['HTTP_USER_AGENT'])
|
24
24
|
|
25
25
|
last_db_update = File::Stat.new(@config['db_file']).mtime
|
26
|
-
resp['
|
26
|
+
resp['Cache-Control'] = 'must-revalidate, max-age=0'
|
27
27
|
resp['ETag'] = "W/\"#{last_db_update.to_i}#{req.params.sort}\""
|
28
|
+
resp['Last-Modified'] = last_db_update.httpdate
|
28
29
|
|
29
30
|
resp.body = Murlsh::UrlBody.new(@config, @db, req)
|
30
31
|
|
@@ -57,11 +58,6 @@ module Murlsh
|
|
57
58
|
resp = Rack::Response.new([mu].to_json, 200, {
|
58
59
|
'Content-Type' => 'application/json' })
|
59
60
|
|
60
|
-
resp.set_cookie('auth',
|
61
|
-
:expires => Time.mktime(2015, 6, 22),
|
62
|
-
:path => '/',
|
63
|
-
:value => auth)
|
64
|
-
|
65
61
|
resp
|
66
62
|
else
|
67
63
|
Rack::Response.new('Permission denied', 403, {
|
data/murlsh.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{murlsh}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.8.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Matthew M. Boedicker"]
|
12
|
-
s.date = %q{2010-04-
|
12
|
+
s.date = %q{2010-04-25}
|
13
13
|
s.default_executable = %q{murlsh}
|
14
14
|
s.description = %q{url sharing site framework with easy adding, title lookup, atom feed, thumbnails and embedding}
|
15
15
|
s.email = %q{matthewm@boedicker.org}
|
@@ -34,12 +34,15 @@ Gem::Specification.new do |s|
|
|
34
34
|
"lib/murlsh/doc.rb",
|
35
35
|
"lib/murlsh/etag_add_encoding.rb",
|
36
36
|
"lib/murlsh/failproof.rb",
|
37
|
+
"lib/murlsh/far_future_expires.rb",
|
37
38
|
"lib/murlsh/flickr_server.rb",
|
38
39
|
"lib/murlsh/markup.rb",
|
40
|
+
"lib/murlsh/must_revalidate.rb",
|
39
41
|
"lib/murlsh/openlock.rb",
|
40
42
|
"lib/murlsh/plugin.rb",
|
41
43
|
"lib/murlsh/sqlite3_adapter.rb",
|
42
44
|
"lib/murlsh/time_ago.rb",
|
45
|
+
"lib/murlsh/twitter_server.rb",
|
43
46
|
"lib/murlsh/uri.rb",
|
44
47
|
"lib/murlsh/uri_ask.rb",
|
45
48
|
"lib/murlsh/url.rb",
|
@@ -52,6 +55,7 @@ Gem::Specification.new do |s|
|
|
52
55
|
"plugins/add_post_50_update_rss.rb",
|
53
56
|
"plugins/add_post_60_notify_hubs.rb",
|
54
57
|
"plugins/add_pre_50_lookup_content_type_title.rb",
|
58
|
+
"plugins/add_pre_60_github_title.rb",
|
55
59
|
"plugins/hostrec_50_redundant.rb",
|
56
60
|
"plugins/hostrec_60_skip.rb",
|
57
61
|
"plugins/time_50_ago.rb",
|
@@ -59,7 +63,6 @@ Gem::Specification.new do |s|
|
|
59
63
|
"public/css/jquery.jgrowl.css",
|
60
64
|
"public/css/screen.css",
|
61
65
|
"public/js/jquery-1.4.2.min.js",
|
62
|
-
"public/js/jquery.cookie.js",
|
63
66
|
"public/js/jquery.jgrowl_compressed.js",
|
64
67
|
"public/js/js.js",
|
65
68
|
"public/swf/player_mp3_mini.swf",
|
@@ -104,6 +107,7 @@ Gem::Specification.new do |s|
|
|
104
107
|
s.add_runtime_dependency(%q<htmlentities>, [">= 4.2.0"])
|
105
108
|
s.add_runtime_dependency(%q<json>, [">= 1.2.3"])
|
106
109
|
s.add_runtime_dependency(%q<rack>, [">= 1.0.0"])
|
110
|
+
s.add_runtime_dependency(%q<rack-cache>, [">= 0.5.2"])
|
107
111
|
s.add_runtime_dependency(%q<sqlite3-ruby>, [">= 1.2.1"])
|
108
112
|
else
|
109
113
|
s.add_dependency(%q<activerecord>, [">= 2.3.4"])
|
@@ -113,6 +117,7 @@ Gem::Specification.new do |s|
|
|
113
117
|
s.add_dependency(%q<htmlentities>, [">= 4.2.0"])
|
114
118
|
s.add_dependency(%q<json>, [">= 1.2.3"])
|
115
119
|
s.add_dependency(%q<rack>, [">= 1.0.0"])
|
120
|
+
s.add_dependency(%q<rack-cache>, [">= 0.5.2"])
|
116
121
|
s.add_dependency(%q<sqlite3-ruby>, [">= 1.2.1"])
|
117
122
|
end
|
118
123
|
else
|
@@ -123,6 +128,7 @@ Gem::Specification.new do |s|
|
|
123
128
|
s.add_dependency(%q<htmlentities>, [">= 4.2.0"])
|
124
129
|
s.add_dependency(%q<json>, [">= 1.2.3"])
|
125
130
|
s.add_dependency(%q<rack>, [">= 1.0.0"])
|
131
|
+
s.add_dependency(%q<rack-cache>, [">= 0.5.2"])
|
126
132
|
s.add_dependency(%q<sqlite3-ruby>, [">= 1.2.1"])
|
127
133
|
end
|
128
134
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
%w{
|
2
|
+
murlsh
|
3
|
+
}.each { |m| require m }
|
4
|
+
|
5
|
+
module Murlsh
|
6
|
+
|
7
|
+
# Github project page titles are not very descriptive so add meta description
|
8
|
+
# to title.
|
9
|
+
class AddPre60GithubTitle < Plugin
|
10
|
+
|
11
|
+
Hook = 'add_pre'
|
12
|
+
|
13
|
+
def self.run(url, config)
|
14
|
+
if url.url[%r{http://github.com/\w+/\w+}]
|
15
|
+
ask = URI(url.url).extend(Murlsh::UriAsk)
|
16
|
+
url.title << " - #{ask.description}" unless ask.description.empty?
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
data/public/js/js.js
CHANGED
@@ -170,7 +170,7 @@ Murlsh.hrefRes = {
|
|
170
170
|
s3 :
|
171
171
|
/^(http:\/\/static\.mmb\.s3\.amazonaws\.com\/[\w\-]+\.)(jpe?g|gif|pdf|png)$/i,
|
172
172
|
twitter :
|
173
|
-
/^http:\/\/twitter\.com\/\w+\/
|
173
|
+
/^http:\/\/twitter\.com\/\w+\/status(?:es)?\/(\d+)$/i,
|
174
174
|
vimeo :
|
175
175
|
/^http:\/\/(?:www\.)?vimeo\.com\/(\d+)$/i,
|
176
176
|
youtube :
|
@@ -236,8 +236,9 @@ Murlsh.addExtra = function() {
|
|
236
236
|
}
|
237
237
|
} else if (match.twitter) {
|
238
238
|
$.ajax({
|
239
|
-
url : 'http://api.twitter.com/1/statuses/show/' +
|
240
|
-
|
239
|
+
// url : 'http://api.twitter.com/1/statuses/show/' +
|
240
|
+
url : '/twitter/1/statuses/show/' +
|
241
|
+
match.twitter[1] + '.json',
|
241
242
|
dataType : 'jsonp',
|
242
243
|
success : function(d) {
|
243
244
|
var nameLink = $('<a />', {
|
@@ -254,7 +255,8 @@ Murlsh.addExtra = function() {
|
|
254
255
|
width : '48'
|
255
256
|
}), null, nameLink);
|
256
257
|
},
|
257
|
-
context : $(this)
|
258
|
+
context : $(this),
|
259
|
+
jsonpCallback : 'twitterCallback' + match.twitter[1]
|
258
260
|
});
|
259
261
|
} else if (match.vimeo) {
|
260
262
|
$.ajax({
|
@@ -318,6 +320,10 @@ Murlsh.iphoneInit = function() {
|
|
318
320
|
href : '#bottom',
|
319
321
|
text : 'bottom'
|
320
322
|
}));
|
323
|
+
|
324
|
+
// change input types to html5 to make typing easier
|
325
|
+
$('#q').attr('type', 'search');
|
326
|
+
$('#url,#via').attr('type', 'url');
|
321
327
|
};
|
322
328
|
|
323
329
|
$(document).ready(function() {
|
@@ -342,7 +348,4 @@ $(document).ready(function() {
|
|
342
348
|
}, 'json');
|
343
349
|
});
|
344
350
|
|
345
|
-
if ($.cookie('auth')) {
|
346
|
-
$('#auth').val($.cookie('auth'));
|
347
|
-
}
|
348
351
|
});
|
data/spec/doc_spec.rb
CHANGED
@@ -8,10 +8,13 @@ murlsh
|
|
8
8
|
|
9
9
|
describe Murlsh::Doc do
|
10
10
|
|
11
|
-
|
12
|
-
|
11
|
+
context 'when html has everything' do
|
12
|
+
subject do
|
13
|
+
html = <<eos
|
13
14
|
<html>
|
14
15
|
<head>
|
16
|
+
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
|
17
|
+
<meta name="description" content="the description" />
|
15
18
|
<title>the title</title>
|
16
19
|
</head>
|
17
20
|
<body>
|
@@ -19,9 +22,31 @@ describe Murlsh::Doc do
|
|
19
22
|
</body>
|
20
23
|
</html>
|
21
24
|
eos
|
25
|
+
Hpricot(html).extend(Murlsh::Doc)
|
26
|
+
end
|
22
27
|
|
23
|
-
|
24
|
-
|
28
|
+
its(:charset) { should == 'utf-8' }
|
29
|
+
its(:title) { should == 'the title' }
|
30
|
+
its(:description) { should == 'the description' }
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'when html has nothing' do
|
34
|
+
subject do
|
35
|
+
html = <<eos
|
36
|
+
<html>
|
37
|
+
<head>
|
38
|
+
</head>
|
39
|
+
<body>
|
40
|
+
<h1>hi</h1>
|
41
|
+
</body>
|
42
|
+
</html>
|
43
|
+
eos
|
44
|
+
Hpricot(html).extend(Murlsh::Doc)
|
45
|
+
end
|
46
|
+
|
47
|
+
its(:charset) { should be_nil }
|
48
|
+
its(:title) { should be_nil }
|
49
|
+
its(:description) { should be_nil }
|
25
50
|
end
|
26
51
|
|
27
52
|
end
|
@@ -6,7 +6,7 @@ murlsh
|
|
6
6
|
|
7
7
|
describe Murlsh::YamlOrderedHash do
|
8
8
|
|
9
|
-
|
9
|
+
subject do
|
10
10
|
h = {
|
11
11
|
'd' => 4,
|
12
12
|
'a' => 1,
|
@@ -15,14 +15,15 @@ describe Murlsh::YamlOrderedHash do
|
|
15
15
|
}
|
16
16
|
|
17
17
|
h.extend(Murlsh::YamlOrderedHash)
|
18
|
-
|
19
|
-
|
18
|
+
end
|
19
|
+
|
20
|
+
its(:to_yaml) { should == <<EOS
|
20
21
|
---
|
21
22
|
a: 1
|
22
23
|
b: 2
|
23
24
|
c: 3
|
24
25
|
d: 4
|
25
26
|
EOS
|
26
|
-
|
27
|
+
}
|
27
28
|
|
28
29
|
end
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
7
|
+
- 8
|
8
8
|
- 0
|
9
|
-
version: 0.
|
9
|
+
version: 0.8.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Matthew M. Boedicker
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-04-
|
17
|
+
date: 2010-04-25 00:00:00 -04:00
|
18
18
|
default_executable: murlsh
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -116,9 +116,23 @@ dependencies:
|
|
116
116
|
type: :runtime
|
117
117
|
version_requirements: *id007
|
118
118
|
- !ruby/object:Gem::Dependency
|
119
|
-
name:
|
119
|
+
name: rack-cache
|
120
120
|
prerelease: false
|
121
121
|
requirement: &id008 !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - ">="
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
segments:
|
126
|
+
- 0
|
127
|
+
- 5
|
128
|
+
- 2
|
129
|
+
version: 0.5.2
|
130
|
+
type: :runtime
|
131
|
+
version_requirements: *id008
|
132
|
+
- !ruby/object:Gem::Dependency
|
133
|
+
name: sqlite3-ruby
|
134
|
+
prerelease: false
|
135
|
+
requirement: &id009 !ruby/object:Gem::Requirement
|
122
136
|
requirements:
|
123
137
|
- - ">="
|
124
138
|
- !ruby/object:Gem::Version
|
@@ -128,7 +142,7 @@ dependencies:
|
|
128
142
|
- 1
|
129
143
|
version: 1.2.1
|
130
144
|
type: :runtime
|
131
|
-
version_requirements: *
|
145
|
+
version_requirements: *id009
|
132
146
|
description: url sharing site framework with easy adding, title lookup, atom feed, thumbnails and embedding
|
133
147
|
email: matthewm@boedicker.org
|
134
148
|
executables:
|
@@ -154,12 +168,15 @@ files:
|
|
154
168
|
- lib/murlsh/doc.rb
|
155
169
|
- lib/murlsh/etag_add_encoding.rb
|
156
170
|
- lib/murlsh/failproof.rb
|
171
|
+
- lib/murlsh/far_future_expires.rb
|
157
172
|
- lib/murlsh/flickr_server.rb
|
158
173
|
- lib/murlsh/markup.rb
|
174
|
+
- lib/murlsh/must_revalidate.rb
|
159
175
|
- lib/murlsh/openlock.rb
|
160
176
|
- lib/murlsh/plugin.rb
|
161
177
|
- lib/murlsh/sqlite3_adapter.rb
|
162
178
|
- lib/murlsh/time_ago.rb
|
179
|
+
- lib/murlsh/twitter_server.rb
|
163
180
|
- lib/murlsh/uri.rb
|
164
181
|
- lib/murlsh/uri_ask.rb
|
165
182
|
- lib/murlsh/url.rb
|
@@ -172,6 +189,7 @@ files:
|
|
172
189
|
- plugins/add_post_50_update_rss.rb
|
173
190
|
- plugins/add_post_60_notify_hubs.rb
|
174
191
|
- plugins/add_pre_50_lookup_content_type_title.rb
|
192
|
+
- plugins/add_pre_60_github_title.rb
|
175
193
|
- plugins/hostrec_50_redundant.rb
|
176
194
|
- plugins/hostrec_60_skip.rb
|
177
195
|
- plugins/time_50_ago.rb
|
@@ -179,7 +197,6 @@ files:
|
|
179
197
|
- public/css/jquery.jgrowl.css
|
180
198
|
- public/css/screen.css
|
181
199
|
- public/js/jquery-1.4.2.min.js
|
182
|
-
- public/js/jquery.cookie.js
|
183
200
|
- public/js/jquery.jgrowl_compressed.js
|
184
201
|
- public/js/js.js
|
185
202
|
- public/swf/player_mp3_mini.swf
|
data/public/js/jquery.cookie.js
DELETED
@@ -1,96 +0,0 @@
|
|
1
|
-
/**
|
2
|
-
* Cookie plugin
|
3
|
-
*
|
4
|
-
* Copyright (c) 2006 Klaus Hartl (stilbuero.de)
|
5
|
-
* Dual licensed under the MIT and GPL licenses:
|
6
|
-
* http://www.opensource.org/licenses/mit-license.php
|
7
|
-
* http://www.gnu.org/licenses/gpl.html
|
8
|
-
*
|
9
|
-
*/
|
10
|
-
|
11
|
-
/**
|
12
|
-
* Create a cookie with the given name and value and other optional parameters.
|
13
|
-
*
|
14
|
-
* @example $.cookie('the_cookie', 'the_value');
|
15
|
-
* @desc Set the value of a cookie.
|
16
|
-
* @example $.cookie('the_cookie', 'the_value', { expires: 7, path: '/', domain: 'jquery.com', secure: true });
|
17
|
-
* @desc Create a cookie with all available options.
|
18
|
-
* @example $.cookie('the_cookie', 'the_value');
|
19
|
-
* @desc Create a session cookie.
|
20
|
-
* @example $.cookie('the_cookie', null);
|
21
|
-
* @desc Delete a cookie by passing null as value. Keep in mind that you have to use the same path and domain
|
22
|
-
* used when the cookie was set.
|
23
|
-
*
|
24
|
-
* @param String name The name of the cookie.
|
25
|
-
* @param String value The value of the cookie.
|
26
|
-
* @param Object options An object literal containing key/value pairs to provide optional cookie attributes.
|
27
|
-
* @option Number|Date expires Either an integer specifying the expiration date from now on in days or a Date object.
|
28
|
-
* If a negative value is specified (e.g. a date in the past), the cookie will be deleted.
|
29
|
-
* If set to null or omitted, the cookie will be a session cookie and will not be retained
|
30
|
-
* when the the browser exits.
|
31
|
-
* @option String path The value of the path atribute of the cookie (default: path of page that created the cookie).
|
32
|
-
* @option String domain The value of the domain attribute of the cookie (default: domain of page that created the cookie).
|
33
|
-
* @option Boolean secure If true, the secure attribute of the cookie will be set and the cookie transmission will
|
34
|
-
* require a secure protocol (like HTTPS).
|
35
|
-
* @type undefined
|
36
|
-
*
|
37
|
-
* @name $.cookie
|
38
|
-
* @cat Plugins/Cookie
|
39
|
-
* @author Klaus Hartl/klaus.hartl@stilbuero.de
|
40
|
-
*/
|
41
|
-
|
42
|
-
/**
|
43
|
-
* Get the value of a cookie with the given name.
|
44
|
-
*
|
45
|
-
* @example $.cookie('the_cookie');
|
46
|
-
* @desc Get the value of a cookie.
|
47
|
-
*
|
48
|
-
* @param String name The name of the cookie.
|
49
|
-
* @return The value of the cookie.
|
50
|
-
* @type String
|
51
|
-
*
|
52
|
-
* @name $.cookie
|
53
|
-
* @cat Plugins/Cookie
|
54
|
-
* @author Klaus Hartl/klaus.hartl@stilbuero.de
|
55
|
-
*/
|
56
|
-
jQuery.cookie = function(name, value, options) {
|
57
|
-
if (typeof value != 'undefined') { // name and value given, set cookie
|
58
|
-
options = options || {};
|
59
|
-
if (value === null) {
|
60
|
-
value = '';
|
61
|
-
options.expires = -1;
|
62
|
-
}
|
63
|
-
var expires = '';
|
64
|
-
if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) {
|
65
|
-
var date;
|
66
|
-
if (typeof options.expires == 'number') {
|
67
|
-
date = new Date();
|
68
|
-
date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000));
|
69
|
-
} else {
|
70
|
-
date = options.expires;
|
71
|
-
}
|
72
|
-
expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE
|
73
|
-
}
|
74
|
-
// CAUTION: Needed to parenthesize options.path and options.domain
|
75
|
-
// in the following expressions, otherwise they evaluate to undefined
|
76
|
-
// in the packed version for some reason...
|
77
|
-
var path = options.path ? '; path=' + (options.path) : '';
|
78
|
-
var domain = options.domain ? '; domain=' + (options.domain) : '';
|
79
|
-
var secure = options.secure ? '; secure' : '';
|
80
|
-
document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join('');
|
81
|
-
} else { // only name given, get cookie
|
82
|
-
var cookieValue = null;
|
83
|
-
if (document.cookie && document.cookie != '') {
|
84
|
-
var cookies = document.cookie.split(';');
|
85
|
-
for (var i = 0; i < cookies.length; i++) {
|
86
|
-
var cookie = jQuery.trim(cookies[i]);
|
87
|
-
// Does this cookie string begin with the name we want?
|
88
|
-
if (cookie.substring(0, name.length + 1) == (name + '=')) {
|
89
|
-
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
|
90
|
-
break;
|
91
|
-
}
|
92
|
-
}
|
93
|
-
}
|
94
|
-
return cookieValue;
|
95
|
-
}
|
96
|
-
};
|