murlsh 0.7.0 → 0.8.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/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
|
-
};
|