murlsh 0.6.1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/.gitignore +3 -0
  2. data/README.textile +25 -2
  3. data/Rakefile +37 -5
  4. data/VERSION +1 -1
  5. data/bin/murlsh +3 -3
  6. data/config.ru +10 -4
  7. data/config.yaml +14 -10
  8. data/lib/murlsh/atom_feed.rb +5 -4
  9. data/lib/murlsh/auth.rb +6 -7
  10. data/lib/murlsh/dispatch.rb +25 -23
  11. data/lib/murlsh/doc.rb +14 -6
  12. data/lib/murlsh/etag_add_encoding.rb +27 -0
  13. data/lib/murlsh/flickr_server.rb +54 -0
  14. data/lib/murlsh/markup.rb +18 -1
  15. data/lib/murlsh/plugin.rb +2 -0
  16. data/lib/murlsh/sqlite3_adapter.rb +3 -1
  17. data/lib/murlsh/time_ago.rb +27 -0
  18. data/lib/murlsh/uri.rb +3 -1
  19. data/lib/murlsh/uri_ask.rb +13 -10
  20. data/lib/murlsh/url.rb +11 -4
  21. data/lib/murlsh/url_body.rb +21 -31
  22. data/lib/murlsh/url_server.rb +1 -2
  23. data/lib/murlsh/yaml_ordered_hash.rb +22 -0
  24. data/lib/murlsh.rb +4 -18
  25. data/murlsh.gemspec +22 -5
  26. data/plugins/add_post_50_update_feed.rb +4 -2
  27. data/plugins/add_post_50_update_rss.rb +37 -0
  28. data/plugins/add_post_60_notify_hubs.rb +3 -2
  29. data/plugins/add_pre_50_lookup_content_type_title.rb +3 -1
  30. data/plugins/time_50_ago.rb +16 -0
  31. data/plugins/via_50_domain.rb +36 -0
  32. data/public/css/screen.css +5 -6
  33. data/public/js/js.js +142 -94
  34. data/spec/atom_feed_spec.rb +21 -20
  35. data/spec/auth_spec.rb +8 -6
  36. data/spec/dispatch_spec.rb +26 -0
  37. data/spec/doc_spec.rb +27 -0
  38. data/spec/markup_spec.rb +3 -1
  39. data/spec/uri_ask_spec.rb +5 -3
  40. data/spec/uri_spec.rb +3 -1
  41. data/spec/url_spec.rb +52 -0
  42. data/spec/xhtml_response_spec.rb +3 -1
  43. data/spec/yaml_ordered_hash_spec.rb +28 -0
  44. metadata +37 -9
  45. data/lib/murlsh/time.rb +0 -20
@@ -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.collect { |x| "MATCH(#{x}, ?)" }.join(' OR ')].push(
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 mu.name
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(via.domain || via, :href => via)
67
+ text!(' via '); a(display_via, :href => via)
65
68
  }
66
69
  end
67
- span(", #{mu.time.fuzzy}", :class => 'date') if
68
- @config.fetch('show_dates', true) and mu.time
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(:description => @config.fetch('description', ''),
89
- :viewport =>
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
- input(:type => 'text', :id => 'q', :name => 'q', :size => 32,
119
- :value => @q)
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 { add_form_input('Add URL:', 'url', 32) }
130
- self.p { add_form_input('Via:', 'via', 32) }
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
- add_form_input('Password:', 'auth', 16, 'password')
133
- input(:type => 'button', :id => 'submit', :value => 'Add')
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
@@ -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
- require 'murlsh/doc'
2
- require 'murlsh/uri'
3
- require 'murlsh/auth'
4
- require 'murlsh/dispatch'
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.6.1"
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-03-10}
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/time.rb",
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/xhtml_response_spec.rb"
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/uri_spec.rb"
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
- require 'murlsh'
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', []).collect { |x| x['subscribe_url'] } )
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
- require 'murlsh'
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
 
@@ -1,4 +1,6 @@
1
- require 'murlsh'
1
+ %w{
2
+ murlsh
3
+ }.each { |m| require m }
2
4
 
3
5
  module Murlsh
4
6
 
@@ -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
@@ -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 : 1.875em;
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 : 130px;
133
+ width : 110px;
135
134
  }
136
135
 
137
136
  input#url, input#via {