murlsh 0.6.1 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- 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/lib/murlsh/url_body.rb
CHANGED
@@ -20,7 +20,7 @@ module Murlsh
|
|
20
20
|
def search_conditions
|
21
21
|
if @q
|
22
22
|
search_cols = %w{name title url}
|
23
|
-
[search_cols.
|
23
|
+
[search_cols.map { |x| "MATCH(#{x}, ?)" }.join(' OR ')].push(
|
24
24
|
*[@q] * search_cols.size)
|
25
25
|
else
|
26
26
|
[]
|
@@ -51,21 +51,26 @@ module Murlsh
|
|
51
51
|
div(:class => 'icon') {
|
52
52
|
gravatar(mu.email, 's' => gravatar_size, :text => mu.name)
|
53
53
|
} if mu.email and gravatar_size > 0
|
54
|
-
div(mu.name, :class => 'name') if
|
54
|
+
div(mu.name, :class => 'name') if
|
55
|
+
@config.fetch('show_names', false) and mu.name
|
55
56
|
end
|
56
57
|
|
57
|
-
a(mu.title_stripped, :href => mu.url)
|
58
|
+
a(mu.title_stripped, :href => mu.url, :class => 'm')
|
58
59
|
|
59
60
|
mu.hostrec do |hostrec|
|
60
61
|
self.span(" [#{hostrec}]", :class => 'host')
|
61
62
|
end
|
62
63
|
mu.viarec do |via|
|
64
|
+
display_via = Murlsh::Plugin.hooks('via').inject(
|
65
|
+
via) { |result,plugin| plugin.run(result) }
|
63
66
|
span(:class => 'via') {
|
64
|
-
text!(' via '); a(
|
67
|
+
text!(' via '); a(display_via, :href => via)
|
65
68
|
}
|
66
69
|
end
|
67
|
-
|
68
|
-
|
70
|
+
|
71
|
+
display_time = Murlsh::Plugin.hooks('time').inject(
|
72
|
+
mu.time) { |result,plugin| plugin.run(result) }
|
73
|
+
span(", #{display_time}", :class => 'date') if display_time
|
69
74
|
last = mu
|
70
75
|
}
|
71
76
|
end
|
@@ -85,10 +90,8 @@ module Murlsh
|
|
85
90
|
def headd
|
86
91
|
head {
|
87
92
|
titlee
|
88
|
-
metas(
|
89
|
-
|
90
|
-
'width=device-width,minimum-scale=1.0,maximum-scale=1.0')
|
91
|
-
google_verify
|
93
|
+
metas(@config.select { |k,v| k =~ /^meta_tag_/ and v }.
|
94
|
+
map { |k,v| [k.sub('meta_tag_', ''), v] })
|
92
95
|
css(@config['css_compressed'] || @config['css_files'])
|
93
96
|
atom(@config.fetch('feed_file'))
|
94
97
|
}
|
@@ -99,11 +102,6 @@ module Murlsh
|
|
99
102
|
title(@config.fetch('page_title', '') + (@q ? " /#{@q}" : ''))
|
100
103
|
end
|
101
104
|
|
102
|
-
# Google verification link builder.
|
103
|
-
def google_verify
|
104
|
-
(gv = @config.fetch('google_verify')) and metas('verify-v1' => gv)
|
105
|
-
end
|
106
|
-
|
107
105
|
# Feed icon builder.
|
108
106
|
def feed_icon
|
109
107
|
div(:class => 'icon') {
|
@@ -115,9 +113,8 @@ module Murlsh
|
|
115
113
|
def search_form
|
116
114
|
form(:action => '', :method => 'get') {
|
117
115
|
fieldset {
|
118
|
-
|
119
|
-
|
120
|
-
input(:type => 'submit', :value=> 'Regex Search')
|
116
|
+
form_input(:id => 'q', :size => 32, :value => @q)
|
117
|
+
form_input(:type => 'submit', :value => 'Regex Search')
|
121
118
|
}
|
122
119
|
}
|
123
120
|
end
|
@@ -126,26 +123,19 @@ module Murlsh
|
|
126
123
|
def add_form
|
127
124
|
form(:action => '', :method => 'post') {
|
128
125
|
fieldset(:id => 'add') {
|
129
|
-
self.p {
|
130
|
-
self.p {
|
126
|
+
self.p { form_input(:id => 'url', :label => 'Add URL', :size => 32) }
|
127
|
+
self.p { form_input(:id => 'via', :label => 'Via', :size => 32) }
|
131
128
|
self.p {
|
132
|
-
|
133
|
-
|
129
|
+
form_input(:id => 'auth', :label => 'Password', :size => 16,
|
130
|
+
:type => 'password')
|
131
|
+
form_input(:id => 'submit', :type => 'button', :value => 'Add')
|
134
132
|
}
|
135
133
|
}
|
136
134
|
}
|
137
135
|
end
|
138
136
|
|
139
|
-
# Url add form input builder.
|
140
|
-
def add_form_input(label, id, size, tipe='text')
|
141
|
-
label(label, :for => id)
|
142
|
-
input(:type => tipe, :id => id, :name => id, :size => size)
|
143
|
-
end
|
144
|
-
|
145
137
|
# Clear div builder.
|
146
|
-
def clear
|
147
|
-
div(:style => 'clear : both')
|
148
|
-
end
|
138
|
+
def clear; div(:style => 'clear : both'); end
|
149
139
|
|
150
140
|
# Powered by builder.
|
151
141
|
def powered_by
|
data/lib/murlsh/url_server.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
%w{
|
2
|
-
rubygems
|
3
2
|
active_record
|
4
3
|
rack
|
5
4
|
}.each { |m| require m }
|
@@ -25,7 +24,7 @@ module Murlsh
|
|
25
24
|
|
26
25
|
last_db_update = File::Stat.new(@config['db_file']).mtime
|
27
26
|
resp['Last-Modified'] = last_db_update.httpdate
|
28
|
-
resp['ETag'] = "#{last_db_update.to_i}#{req.params.sort}"
|
27
|
+
resp['ETag'] = "W/\"#{last_db_update.to_i}#{req.params.sort}\""
|
29
28
|
|
30
29
|
resp.body = Murlsh::UrlBody.new(@config, @db, req)
|
31
30
|
|
@@ -0,0 +1,22 @@
|
|
1
|
+
%w{
|
2
|
+
yaml
|
3
|
+
}.each { |m| require m }
|
4
|
+
|
5
|
+
module Murlsh
|
6
|
+
|
7
|
+
# Hash mixin to generate yaml with hash keys in sorted order.
|
8
|
+
module YamlOrderedHash
|
9
|
+
|
10
|
+
def to_yaml(opts={})
|
11
|
+
YAML::quick_emit(self, opts) do |out|
|
12
|
+
out.map(taguri, to_yaml_style) do |map|
|
13
|
+
sort { |a,b| a[0].to_s <=> b[0].to_s }.each do |k, v|
|
14
|
+
map.add( k, v )
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
data/lib/murlsh.rb
CHANGED
@@ -1,18 +1,4 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
require
|
5
|
-
require 'murlsh/failproof'
|
6
|
-
require 'murlsh/openlock'
|
7
|
-
require 'murlsh/plugin'
|
8
|
-
require 'murlsh/sqlite3_adapter'
|
9
|
-
require 'murlsh/time'
|
10
|
-
require 'murlsh/url_server'
|
11
|
-
require 'murlsh/url'
|
12
|
-
require 'murlsh/uri_ask'
|
13
|
-
require 'murlsh/xhtml_response'
|
14
|
-
|
15
|
-
# requiring builder before active_record blows up
|
16
|
-
require 'murlsh/atom_feed'
|
17
|
-
require 'murlsh/markup'
|
18
|
-
require 'murlsh/url_body'
|
1
|
+
Dir.glob(File.join(File.dirname(__FILE__), 'murlsh', '*.rb')).
|
2
|
+
map { |f| File.join('murlsh', File.basename(f, '.rb')) }.
|
3
|
+
sort.
|
4
|
+
each { |m| require m }
|
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.7.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-
|
12
|
+
s.date = %q{2010-04-10}
|
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}
|
@@ -32,24 +32,30 @@ Gem::Specification.new do |s|
|
|
32
32
|
"lib/murlsh/auth.rb",
|
33
33
|
"lib/murlsh/dispatch.rb",
|
34
34
|
"lib/murlsh/doc.rb",
|
35
|
+
"lib/murlsh/etag_add_encoding.rb",
|
35
36
|
"lib/murlsh/failproof.rb",
|
37
|
+
"lib/murlsh/flickr_server.rb",
|
36
38
|
"lib/murlsh/markup.rb",
|
37
39
|
"lib/murlsh/openlock.rb",
|
38
40
|
"lib/murlsh/plugin.rb",
|
39
41
|
"lib/murlsh/sqlite3_adapter.rb",
|
40
|
-
"lib/murlsh/
|
42
|
+
"lib/murlsh/time_ago.rb",
|
41
43
|
"lib/murlsh/uri.rb",
|
42
44
|
"lib/murlsh/uri_ask.rb",
|
43
45
|
"lib/murlsh/url.rb",
|
44
46
|
"lib/murlsh/url_body.rb",
|
45
47
|
"lib/murlsh/url_server.rb",
|
46
48
|
"lib/murlsh/xhtml_response.rb",
|
49
|
+
"lib/murlsh/yaml_ordered_hash.rb",
|
47
50
|
"murlsh.gemspec",
|
48
51
|
"plugins/add_post_50_update_feed.rb",
|
52
|
+
"plugins/add_post_50_update_rss.rb",
|
49
53
|
"plugins/add_post_60_notify_hubs.rb",
|
50
54
|
"plugins/add_pre_50_lookup_content_type_title.rb",
|
51
55
|
"plugins/hostrec_50_redundant.rb",
|
52
56
|
"plugins/hostrec_60_skip.rb",
|
57
|
+
"plugins/time_50_ago.rb",
|
58
|
+
"plugins/via_50_domain.rb",
|
53
59
|
"public/css/jquery.jgrowl.css",
|
54
60
|
"public/css/screen.css",
|
55
61
|
"public/js/jquery-1.4.2.min.js",
|
@@ -59,10 +65,14 @@ Gem::Specification.new do |s|
|
|
59
65
|
"public/swf/player_mp3_mini.swf",
|
60
66
|
"spec/atom_feed_spec.rb",
|
61
67
|
"spec/auth_spec.rb",
|
68
|
+
"spec/dispatch_spec.rb",
|
69
|
+
"spec/doc_spec.rb",
|
62
70
|
"spec/markup_spec.rb",
|
63
71
|
"spec/uri_ask_spec.rb",
|
64
72
|
"spec/uri_spec.rb",
|
65
|
-
"spec/
|
73
|
+
"spec/url_spec.rb",
|
74
|
+
"spec/xhtml_response_spec.rb",
|
75
|
+
"spec/yaml_ordered_hash_spec.rb"
|
66
76
|
]
|
67
77
|
s.homepage = %q{http://github.com/mmb/murlsh}
|
68
78
|
s.rdoc_options = ["--charset=UTF-8"]
|
@@ -73,9 +83,13 @@ Gem::Specification.new do |s|
|
|
73
83
|
"spec/xhtml_response_spec.rb",
|
74
84
|
"spec/atom_feed_spec.rb",
|
75
85
|
"spec/auth_spec.rb",
|
86
|
+
"spec/yaml_ordered_hash_spec.rb",
|
76
87
|
"spec/uri_ask_spec.rb",
|
88
|
+
"spec/dispatch_spec.rb",
|
77
89
|
"spec/markup_spec.rb",
|
78
|
-
"spec/
|
90
|
+
"spec/url_spec.rb",
|
91
|
+
"spec/uri_spec.rb",
|
92
|
+
"spec/doc_spec.rb"
|
79
93
|
]
|
80
94
|
|
81
95
|
if s.respond_to? :specification_version then
|
@@ -88,6 +102,7 @@ Gem::Specification.new do |s|
|
|
88
102
|
s.add_runtime_dependency(%q<builder>, [">= 2.1.2"])
|
89
103
|
s.add_runtime_dependency(%q<hpricot>, [">= 0.8.1"])
|
90
104
|
s.add_runtime_dependency(%q<htmlentities>, [">= 4.2.0"])
|
105
|
+
s.add_runtime_dependency(%q<json>, [">= 1.2.3"])
|
91
106
|
s.add_runtime_dependency(%q<rack>, [">= 1.0.0"])
|
92
107
|
s.add_runtime_dependency(%q<sqlite3-ruby>, [">= 1.2.1"])
|
93
108
|
else
|
@@ -96,6 +111,7 @@ Gem::Specification.new do |s|
|
|
96
111
|
s.add_dependency(%q<builder>, [">= 2.1.2"])
|
97
112
|
s.add_dependency(%q<hpricot>, [">= 0.8.1"])
|
98
113
|
s.add_dependency(%q<htmlentities>, [">= 4.2.0"])
|
114
|
+
s.add_dependency(%q<json>, [">= 1.2.3"])
|
99
115
|
s.add_dependency(%q<rack>, [">= 1.0.0"])
|
100
116
|
s.add_dependency(%q<sqlite3-ruby>, [">= 1.2.1"])
|
101
117
|
end
|
@@ -105,6 +121,7 @@ Gem::Specification.new do |s|
|
|
105
121
|
s.add_dependency(%q<builder>, [">= 2.1.2"])
|
106
122
|
s.add_dependency(%q<hpricot>, [">= 0.8.1"])
|
107
123
|
s.add_dependency(%q<htmlentities>, [">= 4.2.0"])
|
124
|
+
s.add_dependency(%q<json>, [">= 1.2.3"])
|
108
125
|
s.add_dependency(%q<rack>, [">= 1.0.0"])
|
109
126
|
s.add_dependency(%q<sqlite3-ruby>, [">= 1.2.1"])
|
110
127
|
end
|
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
%w{
|
2
|
+
murlsh
|
3
|
+
}.each { |m| require m }
|
2
4
|
|
3
5
|
module Murlsh
|
4
6
|
|
@@ -14,7 +16,7 @@ module Murlsh
|
|
14
16
|
feed = Murlsh::AtomFeed.new(config.fetch('root_url'),
|
15
17
|
:filename => config.fetch('feed_file'),
|
16
18
|
:title => config.fetch('page_title', ''),
|
17
|
-
:hubs => config.fetch('pubsubhubbub_hubs', []).
|
19
|
+
:hubs => config.fetch('pubsubhubbub_hubs', []).map { |x| x['subscribe_url'] } )
|
18
20
|
|
19
21
|
feed.write(latest, config.fetch('feed_file'))
|
20
22
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
%w{
|
2
|
+
rss/maker
|
3
|
+
|
4
|
+
murlsh
|
5
|
+
}.each { |m| require m }
|
6
|
+
|
7
|
+
module Murlsh
|
8
|
+
|
9
|
+
# Regenerate RSS feed after a new url has been added.
|
10
|
+
class AddPost50UpdateRss < Plugin
|
11
|
+
|
12
|
+
Hook = 'add_post'
|
13
|
+
|
14
|
+
def self.run(config)
|
15
|
+
output_file = 'rss.xml'
|
16
|
+
|
17
|
+
feed = RSS::Maker.make('2.0') do |f|
|
18
|
+
f.channel.title = f.channel.description = config.fetch('page_title', '')
|
19
|
+
f.channel.link = URI.join(config.fetch('root_url'), output_file)
|
20
|
+
f.items.do_sort = true
|
21
|
+
|
22
|
+
Murlsh::Url.all(:order => 'id DESC',
|
23
|
+
:limit => config.fetch('num_posts_feed', 25)).each do |mu|
|
24
|
+
i = f.items.new_item
|
25
|
+
i.title = mu.title_stripped
|
26
|
+
i.link = mu.url
|
27
|
+
i.date = mu.time
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
Murlsh::openlock(output_file, 'w') { |f| f.write(feed) }
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
%w{
|
2
|
+
murlsh
|
3
|
+
}.each { |m| require m }
|
2
4
|
|
3
5
|
module Murlsh
|
4
6
|
|
@@ -11,7 +13,6 @@ module Murlsh
|
|
11
13
|
hubs = config.fetch('pubsubhubbub_hubs', [])
|
12
14
|
|
13
15
|
unless hubs.empty?
|
14
|
-
require 'rubygems'
|
15
16
|
require 'eventmachine'
|
16
17
|
require 'pubsubhubbub'
|
17
18
|
|
@@ -0,0 +1,16 @@
|
|
1
|
+
%w{
|
2
|
+
murlsh
|
3
|
+
}.each { |m| require m }
|
4
|
+
|
5
|
+
module Murlsh
|
6
|
+
|
7
|
+
# show the time as the fuzzy amount of time that has elapsed since then
|
8
|
+
class Time50Ago < Plugin
|
9
|
+
|
10
|
+
Hook = 'time'
|
11
|
+
|
12
|
+
def self.run(time); time.extend(Murlsh::TimeAgo).ago if time; end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
%w{
|
2
|
+
murlsh
|
3
|
+
}.each { |m| require m }
|
4
|
+
|
5
|
+
module Murlsh
|
6
|
+
|
7
|
+
# Convert a via url to its display text.
|
8
|
+
#
|
9
|
+
# For most urls the domain is displayed, but for some return custom text.
|
10
|
+
class Via50Domain < Plugin
|
11
|
+
|
12
|
+
Hook = 'via'
|
13
|
+
|
14
|
+
def self.run(via)
|
15
|
+
search = via.to_s.gsub(%r{^http://}, '')
|
16
|
+
|
17
|
+
case
|
18
|
+
when m = search.match(%r{^news\.ycombinator\.com}i)
|
19
|
+
'hacker news'
|
20
|
+
when m = search.match(%r{^www\.reddit\.com/r/([a-z\d]+?)/}i)
|
21
|
+
"#{m[1]}.reddit"
|
22
|
+
when m = search.match(%r{^delicious\.com/(\w+)}i)
|
23
|
+
"delicious/#{m[1]}"
|
24
|
+
when m = search.match(%r{^twitter\.com/(\w+)/}i)
|
25
|
+
"twitter/#{m[1]}"
|
26
|
+
when m = search.match(%r{^([a-z\d][a-z\d-]{0,61}[a-z\d])\.tumblr\.com/}i)
|
27
|
+
"#{m[1]}.tumblr"
|
28
|
+
else
|
29
|
+
via.domain || via
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
data/public/css/screen.css
CHANGED
@@ -92,6 +92,8 @@ a.feed {
|
|
92
92
|
font-weight : bold;
|
93
93
|
font-size : 0.75em;
|
94
94
|
padding : 2px 4px 2px 4px;
|
95
|
+
-moz-border-radius : 5px;
|
96
|
+
-webkit-border-radius : 5px;
|
95
97
|
}
|
96
98
|
|
97
99
|
span.date {
|
@@ -109,15 +111,11 @@ div.jGrowl div.jGrowl-closer {
|
|
109
111
|
/* iphone */
|
110
112
|
@media screen and (max-device-width : 480px) {
|
111
113
|
|
112
|
-
a.feed {
|
113
|
-
display : none;
|
114
|
-
}
|
115
|
-
|
116
114
|
body {
|
117
115
|
margin : 0;
|
118
116
|
padding : 0;
|
119
117
|
font-size : 1.25em;
|
120
|
-
line-height :
|
118
|
+
line-height : normal;
|
121
119
|
}
|
122
120
|
|
123
121
|
#urls {
|
@@ -128,10 +126,11 @@ div.jGrowl div.jGrowl-closer {
|
|
128
126
|
|
129
127
|
#urls li {
|
130
128
|
overflow : hidden;
|
129
|
+
border-bottom : 1px solid #ccc;
|
131
130
|
}
|
132
131
|
|
133
132
|
input#q {
|
134
|
-
width :
|
133
|
+
width : 110px;
|
135
134
|
}
|
136
135
|
|
137
136
|
input#url, input#via {
|