murlsh 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.textile CHANGED
@@ -56,6 +56,7 @@ Plugin hooks
56
56
  |Hook|Description|run() arguments|Returns|
57
57
  |add_pre|called before a new url is saved|url, config hash|undefined|
58
58
  |add_post|called after a new url is saved|url, config hash|undefined|
59
+ |avatar|called to get an avatar url from an email md5 sum|avatar url, url, config hash|avatar url|
59
60
  |html_parse|parse HTML using something like Hpricot or Nokogiri|parseable|parsed HTML, only first plugin is run (cannot be chained)|
60
61
  |url_display_add|called to display additional information after urls|markup builder, url, config hash|undefined|
61
62
 
data/Rakefile CHANGED
@@ -404,10 +404,11 @@ begin
404
404
  bcrypt-ruby >= 2.1.2
405
405
  builder >= 2.1.2
406
406
  flickraw >= 0.8.3
407
- hpricot >= 0.8.1
408
407
  htmlentities >= 4.2.0
409
408
  json >= 1.2.3
409
+ nokogiri ~> 1.0
410
410
  plumnailer >= 0.1.0
411
+ public_suffix_service ~> 0.0
411
412
  push-notify >= 0.1.0
412
413
  rack >= 1.0.0
413
414
  rack-cache >= 0.5.2
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.1.0
1
+ 1.2.0
data/config.ru CHANGED
@@ -1,5 +1,6 @@
1
1
  $:.unshift(File.join(File.dirname(__FILE__), 'lib'))
2
2
 
3
+ require 'uri'
3
4
  require 'yaml'
4
5
 
5
6
  require 'rack/cache'
@@ -40,4 +41,6 @@ end
40
41
 
41
42
  # use Rack::Lint
42
43
 
44
+ Dir['plugins/*.rb'].each { |p| require p }
45
+
43
46
  run Murlsh::Dispatch.new(config)
@@ -0,0 +1,10 @@
1
+ module Murlsh
2
+
3
+ module_function
4
+
5
+ # Query string builder. Takes hash of query string variables.
6
+ def build_query(h)
7
+ h.empty? ? '' : '?' + h.map { |k,v| URI.escape "#{k}=#{v}" }.join('&')
8
+ end
9
+
10
+ end
@@ -1,3 +1,5 @@
1
+ require 'uri'
2
+
1
3
  require 'active_record'
2
4
  require 'rack'
3
5
 
@@ -12,29 +14,32 @@ module Murlsh
12
14
  def initialize(config)
13
15
  @config = config
14
16
 
15
- ActiveRecord::Base.establish_connection(
16
- :adapter => 'sqlite3', :database => @config.fetch('db_file'))
17
- ActiveRecord::Base.include_root_in_json = false
18
-
19
- db = ActiveRecord::Base.connection.instance_variable_get(:@connection)
20
-
21
- url_server = Murlsh::UrlServer.new(@config, db)
22
- config_server = Murlsh::ConfigServer.new(@config)
23
-
24
- root_path = URI(@config.fetch('root_url')).path
17
+ url_server = Murlsh::UrlServer.new(config)
18
+ config_server = Murlsh::ConfigServer.new(config)
19
+ root_path = URI(config.fetch('root_url')).path
25
20
 
26
- @dispatch = [
21
+ @routes = [
27
22
  [%r{^HEAD #{root_path}(url)?$}, url_server.method(:head)],
28
23
  [%r{^GET #{root_path}(url)?$}, url_server.method(:get)],
29
24
  [%r{^POST #{root_path}(url)?$}, url_server.method(:post)],
30
25
  [%r{^HEAD #{root_path}config$}, config_server.method(:head)],
31
26
  [%r{^GET #{root_path}config$}, config_server.method(:get)],
32
27
  ]
28
+
29
+ db_init
30
+ end
31
+
32
+ def db_init
33
+ ActiveRecord::Base.establish_connection(
34
+ :adapter => 'sqlite3', :database => @config.fetch('db_file'))
35
+
36
+ ActiveRecord::Base.default_timezone = :utc
37
+ ActiveRecord::Base.include_root_in_json = false
33
38
  end
34
39
 
35
40
  # Figure out which method will handle request.
36
41
  def dispatch(req)
37
- method_match = @dispatch.find do |rule|
42
+ method_match = routes.find do |rule|
38
43
  rule[0].match("#{req.request_method} #{req.path}")
39
44
  end
40
45
 
@@ -56,6 +61,7 @@ module Murlsh
56
61
  404, { 'Content-Type' => 'text/html' }
57
62
  end
58
63
 
64
+ attr_accessor :routes
59
65
  end
60
66
 
61
67
  end
data/lib/murlsh/doc.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  module Murlsh
2
2
 
3
- # Hpricot:Doc mixin.
3
+ # Nokogiri / Hpricot doc mixin.
4
4
  module Doc
5
5
 
6
6
  # Get the character set of the document.
data/lib/murlsh/markup.rb CHANGED
@@ -65,33 +65,6 @@ module Murlsh
65
65
  tags.each { |k,v| meta :name => k, :content => v }
66
66
  end
67
67
 
68
- # Gravatar builder. Takes MD5 hash of email address.
69
- # Options:
70
- # * 'd' - default Gravatar (identicon, monsterid, or wavatar)
71
- # * 's' - size (0 - 512)
72
- # * 'r' - rating (g, pg, r or x)
73
- def gravatar(email_hash, options={})
74
- query = options.reject do |k,v|
75
- not ((k == 'd' and %w{identicon monsterid wavatar}.include?(v)) or
76
- (k =='s' and (0..512).include?(v)) or
77
- (k == 'r' and %w{g pg r x}.include?(v)))
78
- end
79
-
80
- return if query['s'] and query['s'] < 1
81
-
82
- options.reject! { |k,v| %w{d s r}.include? k }
83
- options[:src] = URI.join('http://www.gravatar.com/avatar/',
84
- email_hash + build_query(query))
85
-
86
- murlsh_img options
87
- end
88
-
89
- # Query string builder. Takes hash of query string variables.
90
- def build_query(h)
91
- h.empty? ? '' :
92
- '?' + h.map { |k,v| URI.escape "#{k}=#{v}" }.join('&')
93
- end
94
-
95
68
  # Form input builder.
96
69
  def form_input(options)
97
70
  if options[:id]
data/lib/murlsh/plugin.rb CHANGED
@@ -7,6 +7,8 @@ module Murlsh
7
7
  # run arguments (url, config hash)
8
8
  # * add_post - called after a new url is saved
9
9
  # run arguments (config hash)
10
+ # * avatar - called to get an avatar url from an email md5 sum
11
+ # run arguments (avatar url, url, config hash)
10
12
  # * html_parse - called to parse HTML using something like Hpricot or Nokogiri
11
13
  # run arguments (parseable)
12
14
  # * url_display_add - called to display additional information after urls
@@ -3,7 +3,7 @@ require 'net/https'
3
3
  require 'open-uri'
4
4
  require 'uri'
5
5
 
6
- require 'hpricot'
6
+ require 'nokogiri'
7
7
  require 'htmlentities'
8
8
  require 'iconv'
9
9
 
@@ -80,7 +80,7 @@ module Murlsh
80
80
  self.open(options[:headers]) do |f|
81
81
  html_parse_plugins = Murlsh::Plugin.hooks('html_parse')
82
82
  @doc = if html_parse_plugins.empty?
83
- Hpricot(f).extend(Murlsh::Doc)
83
+ Nokogiri(f).extend(Murlsh::Doc)
84
84
  else
85
85
  html_parse_plugins.first.run(f).extend(Murlsh::Doc)
86
86
  end
@@ -145,7 +145,8 @@ module Murlsh
145
145
  http = Net::HTTP.new(host, port)
146
146
  http.use_ssl = (scheme == 'https')
147
147
 
148
- resp = http.request_head(path_query, request_headers)
148
+ extend(Murlsh::URIGetPathQuery)
149
+ resp = http.request_head(get_path_query, request_headers)
149
150
 
150
151
  if Net::HTTPSuccess === resp
151
152
  response_headers = resp.to_hash
@@ -0,0 +1,22 @@
1
+ require 'public_suffix_service'
2
+
3
+ require 'murlsh'
4
+
5
+ # URI mixin that adds method to get domain.
6
+ module Murlsh
7
+
8
+ module URIDomain
9
+
10
+ # Return the domain.
11
+ def domain
12
+ if host
13
+ Murlsh::failproof do
14
+ parsed = PublicSuffixService.parse(host.downcase)
15
+ "#{parsed.sld}.#{parsed.tld}"
16
+ end
17
+ end
18
+ end
19
+
20
+ end
21
+
22
+ end
@@ -0,0 +1,11 @@
1
+ module Murlsh
2
+
3
+ # URI mixin that adds method to get path and query string.
4
+ module URIGetPathQuery
5
+
6
+ # Return the path and query string.
7
+ def get_path_query; path + (query ? "?#{query}" : ''); end
8
+
9
+ end
10
+
11
+ end
data/lib/murlsh/url.rb CHANGED
@@ -29,6 +29,13 @@ module Murlsh
29
29
  email and name and email == other.email and name == other.name
30
30
  end
31
31
 
32
+ def ask
33
+ if !defined?(@ask) or @ask.to_s != url
34
+ @ask = URI(url).extend(Murlsh::UriAsk)
35
+ end
36
+ @ask
37
+ end
38
+
32
39
  end
33
40
 
34
41
  end
@@ -6,9 +6,9 @@ module Murlsh
6
6
  class UrlBody < Builder::XmlMarkup
7
7
  include Murlsh::Markup
8
8
 
9
- def initialize(config, db, req, content_type='text/html')
10
- @config, @db, @req, @q, @content_type =
11
- config, db, req, req.params['q'], content_type
9
+ def initialize(config, req, content_type='text/html')
10
+ @config, @req, @q, @content_type =
11
+ config, req, req.params['q'], content_type
12
12
  super(:indent => @config['html_indent'] || 0)
13
13
  end
14
14
 
@@ -40,15 +40,17 @@ module Murlsh
40
40
  ul(:id => 'urls') {
41
41
  li { feed_icon ; search_form }
42
42
 
43
- gravatar_size = @config.fetch('gravatar_size', 0)
44
-
45
43
  last = nil
46
44
  urls.each do |mu|
47
45
  li {
48
46
  unless mu.same_author?(last)
47
+ avatar_url = Murlsh::Plugin.hooks('avatar').inject(
48
+ nil) do |url_so_far,plugin|
49
+ plugin.run(url_so_far, mu, @config)
50
+ end
49
51
  div(:class => 'icon') {
50
- gravatar(mu.email, 's' => gravatar_size, :text => mu.name)
51
- } if mu.email and gravatar_size > 0
52
+ murlsh_img :src => avatar_url, :text => mu.name
53
+ } if avatar_url
52
54
  div(mu.name, :class => 'name') if
53
55
  @config.fetch('show_names', false) and mu.name
54
56
  end
@@ -8,11 +8,8 @@ module Murlsh
8
8
 
9
9
  include HeadFromGet
10
10
 
11
- def initialize(config, db)
12
- @config, @db = config, db
13
- ActiveRecord::Base.default_timezone = :utc
14
-
15
- Dir['plugins/*.rb'].each { |p| load p }
11
+ def initialize(config)
12
+ @config = config
16
13
  end
17
14
 
18
15
  # Respond to a GET request. Return a page of urls based on the query
@@ -27,7 +24,7 @@ module Murlsh
27
24
  resp['ETag'] = "W/\"#{last_db_update.to_i}#{req.params.sort}\""
28
25
  resp['Last-Modified'] = last_db_update.httpdate
29
26
 
30
- resp.body = Murlsh::UrlBody.new(@config, @db, req, resp['Content-Type'])
27
+ resp.body = Murlsh::UrlBody.new(@config, req, resp['Content-Type'])
31
28
 
32
29
  resp
33
30
  end
@@ -37,8 +34,6 @@ module Murlsh
37
34
  auth = req.params['auth']
38
35
  if user = auth.empty? ? nil : Murlsh::Auth.new(
39
36
  @config.fetch('auth_file')).auth(auth)
40
- ActiveRecord::Base.establish_connection :adapter => 'sqlite3',
41
- :database => @config.fetch('db_file')
42
37
 
43
38
  mu = Murlsh::Url.new do |u|
44
39
  u.time = Time.now.gmtime
data/lib/murlsh.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require 'murlsh/head_from_get'
2
2
 
3
3
  require 'murlsh/auth'
4
+ require 'murlsh/build_query'
4
5
  require 'murlsh/config_server'
5
6
  require 'murlsh/dispatch'
6
7
  require 'murlsh/doc'
@@ -16,7 +17,8 @@ require 'murlsh/plugin'
16
17
  require 'murlsh/sqlite3_adapter'
17
18
  require 'murlsh/time_ago'
18
19
  require 'murlsh/uri_ask'
19
- require 'murlsh/uri'
20
+ require 'murlsh/uri_domain'
21
+ require 'murlsh/uri_get_path_query'
20
22
  require 'murlsh/url_body'
21
23
  require 'murlsh/url'
22
24
  require 'murlsh/url_server'
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 = "1.1.0"
8
+ s.version = "1.2.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-11-21}
12
+ s.date = %q{2010-12-16}
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}
@@ -28,6 +28,7 @@ Gem::Specification.new do |s|
28
28
  "config.yaml",
29
29
  "lib/murlsh.rb",
30
30
  "lib/murlsh/auth.rb",
31
+ "lib/murlsh/build_query.rb",
31
32
  "lib/murlsh/config_server.rb",
32
33
  "lib/murlsh/dispatch.rb",
33
34
  "lib/murlsh/doc.rb",
@@ -43,8 +44,9 @@ Gem::Specification.new do |s|
43
44
  "lib/murlsh/plugin.rb",
44
45
  "lib/murlsh/sqlite3_adapter.rb",
45
46
  "lib/murlsh/time_ago.rb",
46
- "lib/murlsh/uri.rb",
47
47
  "lib/murlsh/uri_ask.rb",
48
+ "lib/murlsh/uri_domain.rb",
49
+ "lib/murlsh/uri_get_path_query.rb",
48
50
  "lib/murlsh/url.rb",
49
51
  "lib/murlsh/url_body.rb",
50
52
  "lib/murlsh/url_server.rb",
@@ -56,6 +58,8 @@ Gem::Specification.new do |s|
56
58
  "plugins/add_post_60_notify_hubs.rb",
57
59
  "plugins/add_pre_40_convert_mobile.rb",
58
60
  "plugins/add_pre_50_lookup_content_type_title.rb",
61
+ "plugins/add_pre_50_media_thumbnail.rb",
62
+ "plugins/add_pre_50_open_graph_image.rb",
59
63
  "plugins/add_pre_60_flickr.rb",
60
64
  "plugins/add_pre_60_github_title.rb",
61
65
  "plugins/add_pre_60_google_code_title.rb",
@@ -63,10 +67,10 @@ Gem::Specification.new do |s|
63
67
  "plugins/add_pre_60_s3_image.rb",
64
68
  "plugins/add_pre_60_twitter.rb",
65
69
  "plugins/add_pre_60_vimeo.rb",
66
- "plugins/add_pre_60_youtube.rb",
67
70
  "plugins/add_pre_65_html_thumb.rb",
68
71
  "plugins/add_pre_65_img_thumb.rb",
69
- "plugins/html_parse_50_hpricot.rb",
72
+ "plugins/avatar_50_gravatar.rb",
73
+ "plugins/html_parse_50_nokogiri.rb",
70
74
  "plugins/url_display_add_45_audio.rb",
71
75
  "plugins/url_display_add_50_hostrec.rb",
72
76
  "plugins/url_display_add_55_content_type.rb",
@@ -86,7 +90,7 @@ Gem::Specification.new do |s|
86
90
  "spec/img_store_spec.rb",
87
91
  "spec/markup_spec.rb",
88
92
  "spec/uri_ask_spec.rb",
89
- "spec/uri_spec.rb",
93
+ "spec/uri_domain_spec.rb",
90
94
  "spec/url_spec.rb",
91
95
  "spec/yaml_ordered_hash_spec.rb"
92
96
  ]
@@ -101,7 +105,7 @@ Gem::Specification.new do |s|
101
105
  "spec/img_store_spec.rb",
102
106
  "spec/markup_spec.rb",
103
107
  "spec/uri_ask_spec.rb",
104
- "spec/uri_spec.rb",
108
+ "spec/uri_domain_spec.rb",
105
109
  "spec/url_spec.rb",
106
110
  "spec/yaml_ordered_hash_spec.rb"
107
111
  ]
@@ -115,10 +119,11 @@ Gem::Specification.new do |s|
115
119
  s.add_runtime_dependency(%q<bcrypt-ruby>, [">= 2.1.2"])
116
120
  s.add_runtime_dependency(%q<builder>, [">= 2.1.2"])
117
121
  s.add_runtime_dependency(%q<flickraw>, [">= 0.8.3"])
118
- s.add_runtime_dependency(%q<hpricot>, [">= 0.8.1"])
119
122
  s.add_runtime_dependency(%q<htmlentities>, [">= 4.2.0"])
120
123
  s.add_runtime_dependency(%q<json>, [">= 1.2.3"])
124
+ s.add_runtime_dependency(%q<nokogiri>, ["~> 1.0"])
121
125
  s.add_runtime_dependency(%q<plumnailer>, [">= 0.1.0"])
126
+ s.add_runtime_dependency(%q<public_suffix_service>, ["~> 0.0"])
122
127
  s.add_runtime_dependency(%q<push-notify>, [">= 0.1.0"])
123
128
  s.add_runtime_dependency(%q<rack>, [">= 1.0.0"])
124
129
  s.add_runtime_dependency(%q<rack-cache>, [">= 0.5.2"])
@@ -136,10 +141,11 @@ Gem::Specification.new do |s|
136
141
  s.add_dependency(%q<bcrypt-ruby>, [">= 2.1.2"])
137
142
  s.add_dependency(%q<builder>, [">= 2.1.2"])
138
143
  s.add_dependency(%q<flickraw>, [">= 0.8.3"])
139
- s.add_dependency(%q<hpricot>, [">= 0.8.1"])
140
144
  s.add_dependency(%q<htmlentities>, [">= 4.2.0"])
141
145
  s.add_dependency(%q<json>, [">= 1.2.3"])
146
+ s.add_dependency(%q<nokogiri>, ["~> 1.0"])
142
147
  s.add_dependency(%q<plumnailer>, [">= 0.1.0"])
148
+ s.add_dependency(%q<public_suffix_service>, ["~> 0.0"])
143
149
  s.add_dependency(%q<push-notify>, [">= 0.1.0"])
144
150
  s.add_dependency(%q<rack>, [">= 1.0.0"])
145
151
  s.add_dependency(%q<rack-cache>, [">= 0.5.2"])
@@ -158,10 +164,11 @@ Gem::Specification.new do |s|
158
164
  s.add_dependency(%q<bcrypt-ruby>, [">= 2.1.2"])
159
165
  s.add_dependency(%q<builder>, [">= 2.1.2"])
160
166
  s.add_dependency(%q<flickraw>, [">= 0.8.3"])
161
- s.add_dependency(%q<hpricot>, [">= 0.8.1"])
162
167
  s.add_dependency(%q<htmlentities>, [">= 4.2.0"])
163
168
  s.add_dependency(%q<json>, [">= 1.2.3"])
169
+ s.add_dependency(%q<nokogiri>, ["~> 1.0"])
164
170
  s.add_dependency(%q<plumnailer>, [">= 0.1.0"])
171
+ s.add_dependency(%q<public_suffix_service>, ["~> 0.0"])
165
172
  s.add_dependency(%q<push-notify>, [">= 0.1.0"])
166
173
  s.add_dependency(%q<rack>, [">= 1.0.0"])
167
174
  s.add_dependency(%q<rack-cache>, [">= 0.5.2"])
@@ -65,7 +65,7 @@ module Murlsh
65
65
  options.merge!(
66
66
  :via_type => 'text/html',
67
67
  :via_href => mu.via,
68
- :via_title => URI(mu.via).domain
68
+ :via_title => URI(mu.via).extend(Murlsh::URIDomain).domain
69
69
  )
70
70
  end
71
71
  end
@@ -10,17 +10,16 @@ module Murlsh
10
10
  @hook = 'add_pre'
11
11
 
12
12
  def self.run(url, config)
13
- ask = URI(url.url).extend(Murlsh::UriAsk)
14
13
  headers = {}
15
14
  headers['User-Agent'] = config['user_agent'] if config['user_agent']
16
15
 
17
- content_length = ask.content_length(:headers => headers)
16
+ content_length = url.ask.content_length(:headers => headers)
18
17
  if content_length and not content_length.empty?
19
18
  url.content_length = content_length
20
19
  end
21
20
 
22
- url.content_type = ask.content_type(:headers => headers)
23
- url.title = ask.title(:headers => headers)
21
+ url.content_type = url.ask.content_type(:headers => headers)
22
+ url.title = url.ask.title(:headers => headers)
24
23
  end
25
24
 
26
25
  end
@@ -0,0 +1,36 @@
1
+ require 'cgi'
2
+
3
+ require 'murlsh'
4
+
5
+ module Murlsh
6
+
7
+ # If document has <meta rel="media:thumbnail"> use it as the thumbnail.
8
+ class AddPre50MediaThumbnail < Plugin
9
+
10
+ @hook = 'add_pre'
11
+
12
+ StorageDir = File.join(File.dirname(__FILE__), '..', 'public', 'img',
13
+ 'thumb')
14
+
15
+ def self.run(url, config)
16
+ if not url.thumbnail_url and url.ask.doc
17
+ url.ask.doc.xpath_search("//meta[@rel='media:thumbnail']") do |node|
18
+ if node and node['href'] and not node['href'].empty?
19
+ Murlsh::failproof do
20
+ thumb_storage = Murlsh::ImgStore.new(StorageDir,
21
+ :user_agent => config['user_agent'])
22
+
23
+ stored_filename = thumb_storage.store_url(node['href']) do |i|
24
+ max_side = config.fetch('thumbnail_max_side', 90)
25
+ i.extend(Murlsh::ImageList).resize_down!(max_side)
26
+ end
27
+ url.thumbnail_url = "img/thumb/#{CGI.escape(stored_filename)}"
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ end
35
+
36
+ end
@@ -0,0 +1,36 @@
1
+ require 'cgi'
2
+
3
+ require 'murlsh'
4
+
5
+ module Murlsh
6
+
7
+ # If document has <meta property="og:image"> use it as the thumbnail.
8
+ class AddPre50OpenGraphImage < Plugin
9
+
10
+ @hook = 'add_pre'
11
+
12
+ StorageDir = File.join(File.dirname(__FILE__), '..', 'public', 'img',
13
+ 'thumb')
14
+
15
+ def self.run(url, config)
16
+ if not url.thumbnail_url and url.ask.doc
17
+ url.ask.doc.xpath_search("//meta[@property='og:image']") do |node|
18
+ if node and node['content'] and not node['content'].empty?
19
+ Murlsh::failproof do
20
+ thumb_storage = Murlsh::ImgStore.new(StorageDir,
21
+ :user_agent => config['user_agent'])
22
+
23
+ stored_filename = thumb_storage.store_url(node['content']) do |i|
24
+ max_side = config.fetch('thumbnail_max_side', 90)
25
+ i.extend(Murlsh::ImageList).resize_down!(max_side)
26
+ end
27
+ url.thumbnail_url = "img/thumb/#{CGI.escape(stored_filename)}"
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ end
35
+
36
+ end
@@ -14,8 +14,9 @@ module Murlsh
14
14
 
15
15
  def self.run(url, config)
16
16
  if url.url[GithubRe]
17
- ask = URI(url.url).extend(Murlsh::UriAsk)
18
- url.title << " - #{ask.description}" unless ask.description.empty?
17
+ unless url.ask.description.empty?
18
+ url.title << " - #{url.ask.description}"
19
+ end
19
20
  end
20
21
  end
21
22
 
@@ -13,11 +13,10 @@ module Murlsh
13
13
  GoogleCodeRe = %r{^http://code\.google\.com/p/[\w-]+/$}i
14
14
 
15
15
  def self.run(url, config)
16
- if url.url[GoogleCodeRe]
17
- ask = URI(url.url).extend(Murlsh::UriAsk)
18
- ask.doc.xpath_search("//a[@id='project_summary_link']") do |node|
16
+ if url.url[GoogleCodeRe] and url.ask.doc
17
+ url.ask.doc.xpath_search("//a[@id='project_summary_link']") do |node|
19
18
  summary = node ? node.inner_html : nil
20
- url.title << " - #{ask.decode(summary)}" unless not summary or
19
+ url.title << " - #{url.ask.decode(summary)}" unless not summary or
21
20
  summary.empty?
22
21
  end
23
22
  end
@@ -0,0 +1,22 @@
1
+ require 'uri'
2
+
3
+ require 'murlsh'
4
+
5
+ module Murlsh
6
+
7
+ # Get Gravatar url from a url.
8
+ class Avatar50Gravatar < Plugin
9
+
10
+ @hook = 'avatar'
11
+
12
+ def self.run(avatar_url, url, config)
13
+ if url.email and not url.email.empty? and
14
+ (gravatar_size = config.fetch('gravatar_size', 0)) > 0
15
+ query = { :s => gravatar_size }
16
+ URI.join('http://www.gravatar.com/avatar/', url.email, Murlsh::build_query(query))
17
+ end
18
+ end
19
+
20
+ end
21
+
22
+ end
@@ -0,0 +1,16 @@
1
+ require 'nokogiri'
2
+
3
+ require 'murlsh'
4
+
5
+ module Murlsh
6
+
7
+ # Parse HTML with Nokogiri and return a Nokogiri doc.
8
+ class HtmlParse50Nokogiri < Plugin
9
+
10
+ @hook = 'html_parse'
11
+
12
+ def self.run(x); Nokogiri(x); end
13
+
14
+ end
15
+
16
+ end
@@ -21,7 +21,10 @@ module Murlsh
21
21
 
22
22
  # Show the domain of the url.
23
23
  def self.run(markup, url, config)
24
- if domain = Murlsh::failproof { URI(url.url).domain }
24
+ domain = Murlsh::failproof do
25
+ URI(url.url).extend(Murlsh::URIDomain).domain
26
+ end
27
+ if domain
25
28
  # show domain if not already contained in title and not on skip list
26
29
  unless (url.title and url.title.downcase.index(domain)) or
27
30
  SkipDomains.include?(domain)
@@ -29,7 +29,7 @@ module Murlsh
29
29
  when m = search.match(DeliciousRe); "delicious/#{m[1]}"
30
30
  when m = search.match(TwitterRe); "twitter/#{m[1]}"
31
31
  when m = search.match(TumblrRe); "#{m[1]}.tumblr"
32
- else via_uri.domain || via_uri_s
32
+ else via_uri.extend(Murlsh::URIDomain).domain || via_uri_s
33
33
  end
34
34
 
35
35
  markup.span(:class => 'via') do
data/spec/doc_spec.rb CHANGED
@@ -1,4 +1,4 @@
1
- require 'hpricot'
1
+ require 'nokogiri'
2
2
 
3
3
  require 'murlsh'
4
4
 
@@ -18,7 +18,7 @@ describe Murlsh::Doc do
18
18
  </body>
19
19
  </html>
20
20
  eos
21
- Hpricot(html).extend(Murlsh::Doc)
21
+ Nokogiri(html).extend(Murlsh::Doc)
22
22
  end
23
23
 
24
24
  its(:charset) { should == 'utf-8' }
@@ -37,7 +37,7 @@ eos
37
37
  </body>
38
38
  </html>
39
39
  eos
40
- Hpricot(html).extend(Murlsh::Doc)
40
+ Nokogiri(html).extend(Murlsh::Doc)
41
41
  end
42
42
 
43
43
  its(:charset) { should be_nil }
data/spec/markup_spec.rb CHANGED
@@ -80,52 +80,4 @@ describe MarkupMixer do
80
80
  ].each { |r| @m.target!.should match(/#{r}/) }
81
81
  end
82
82
 
83
- it 'should correctly render a gravatar tag' do
84
- @m.gravatar 'xxx'
85
- @m.target!.should == '<img src="http://www.gravatar.com/avatar/xxx"/>'
86
- end
87
-
88
- it 'should correctly render a gravatar tag with a valid default' do
89
- @m.gravatar 'xxx', 'd' => 'identicon'
90
- @m.target!.should ==
91
- '<img src="http://www.gravatar.com/avatar/xxx?d=identicon"/>'
92
- end
93
-
94
- it 'should not pass the default parameter to gravatar if the default is invalid' do
95
- @m.gravatar 'xxx', 'd' => 'bad'
96
- @m.target!.should == '<img src="http://www.gravatar.com/avatar/xxx"/>'
97
- end
98
-
99
- it 'should correctly render a gravatar tag with a valid rating' do
100
- @m.gravatar 'xxx', 'r' => 'x'
101
- @m.target!.should == '<img src="http://www.gravatar.com/avatar/xxx?r=x"/>'
102
- end
103
-
104
- it 'should not pass the rating parameter to gravatar if the rating is invalid' do
105
- @m.gravatar 'xxx', 'r' => 'foo'
106
- @m.target!.should == '<img src="http://www.gravatar.com/avatar/xxx"/>'
107
- end
108
-
109
- it 'should correctly render a gravatar tag with a valid size' do
110
- @m.gravatar 'xxx', 's' => 100
111
- @m.target!.should ==
112
- '<img src="http://www.gravatar.com/avatar/xxx?s=100"/>'
113
- end
114
-
115
- it 'should not pass the size parameter to gravatar if the size is invalid' do
116
- @m.gravatar 'xxx', 's' => 1000
117
- @m.target!.should == '<img src="http://www.gravatar.com/avatar/xxx"/>'
118
- end
119
-
120
- it 'should return an empty string for a gravatar with size 0' do
121
- @m.gravatar 'xxx', 's' => 0
122
- @m.target!.should be_empty
123
- end
124
-
125
- it 'should correctly render a gravatar tag with an href' do
126
- @m.gravatar 'xxx', :href => '/test/'
127
- @m.target!.should ==
128
- '<a href="/test/"><img src="http://www.gravatar.com/avatar/xxx"/></a>'
129
- end
130
-
131
83
  end
data/spec/uri_ask_spec.rb CHANGED
@@ -2,7 +2,7 @@ require 'uri'
2
2
 
3
3
  require 'murlsh'
4
4
 
5
- Dir['plugins/*.rb'].each { |p| load p }
5
+ Dir['plugins/*.rb'].each { |p| require p }
6
6
 
7
7
  describe Murlsh::UriAsk do
8
8
 
@@ -0,0 +1,22 @@
1
+ require 'uri'
2
+
3
+ require 'murlsh'
4
+
5
+ describe Murlsh::URIDomain do
6
+
7
+ def uri_domain(s); URI(s).extend(Murlsh::URIDomain).domain; end
8
+
9
+ it 'should have its domain set to the domain of its URI if it is a valid HTTP URI' do
10
+ uri_domain('http://foo.com/').should == 'foo.com'
11
+ end
12
+
13
+ it 'should have its domain set nil if it is not a valid HTTP URI' do
14
+ uri_domain('foo').should be_nil
15
+ uri_domain('http://foo.bar/').should be_nil
16
+ end
17
+
18
+ it 'should handle two letter top-level domains' do
19
+ uri_domain('http://www.linux.fm/').should == 'linux.fm'
20
+ end
21
+
22
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: murlsh
3
3
  version: !ruby/object:Gem::Version
4
- hash: 19
4
+ hash: 31
5
5
  prerelease: false
6
6
  segments:
7
7
  - 1
8
- - 1
8
+ - 2
9
9
  - 0
10
- version: 1.1.0
10
+ version: 1.2.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Matthew M. Boedicker
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-11-21 00:00:00 -05:00
18
+ date: 2010-12-16 00:00:00 -05:00
19
19
  default_executable: murlsh
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -83,51 +83,50 @@ dependencies:
83
83
  type: :runtime
84
84
  version_requirements: *id004
85
85
  - !ruby/object:Gem::Dependency
86
- name: hpricot
86
+ name: htmlentities
87
87
  prerelease: false
88
88
  requirement: &id005 !ruby/object:Gem::Requirement
89
89
  none: false
90
90
  requirements:
91
91
  - - ">="
92
92
  - !ruby/object:Gem::Version
93
- hash: 61
93
+ hash: 55
94
94
  segments:
95
+ - 4
96
+ - 2
95
97
  - 0
96
- - 8
97
- - 1
98
- version: 0.8.1
98
+ version: 4.2.0
99
99
  type: :runtime
100
100
  version_requirements: *id005
101
101
  - !ruby/object:Gem::Dependency
102
- name: htmlentities
102
+ name: json
103
103
  prerelease: false
104
104
  requirement: &id006 !ruby/object:Gem::Requirement
105
105
  none: false
106
106
  requirements:
107
107
  - - ">="
108
108
  - !ruby/object:Gem::Version
109
- hash: 55
109
+ hash: 25
110
110
  segments:
111
- - 4
111
+ - 1
112
112
  - 2
113
- - 0
114
- version: 4.2.0
113
+ - 3
114
+ version: 1.2.3
115
115
  type: :runtime
116
116
  version_requirements: *id006
117
117
  - !ruby/object:Gem::Dependency
118
- name: json
118
+ name: nokogiri
119
119
  prerelease: false
120
120
  requirement: &id007 !ruby/object:Gem::Requirement
121
121
  none: false
122
122
  requirements:
123
- - - ">="
123
+ - - ~>
124
124
  - !ruby/object:Gem::Version
125
- hash: 25
125
+ hash: 15
126
126
  segments:
127
127
  - 1
128
- - 2
129
- - 3
130
- version: 1.2.3
128
+ - 0
129
+ version: "1.0"
131
130
  type: :runtime
132
131
  version_requirements: *id007
133
132
  - !ruby/object:Gem::Dependency
@@ -147,9 +146,24 @@ dependencies:
147
146
  type: :runtime
148
147
  version_requirements: *id008
149
148
  - !ruby/object:Gem::Dependency
150
- name: push-notify
149
+ name: public_suffix_service
151
150
  prerelease: false
152
151
  requirement: &id009 !ruby/object:Gem::Requirement
152
+ none: false
153
+ requirements:
154
+ - - ~>
155
+ - !ruby/object:Gem::Version
156
+ hash: 11
157
+ segments:
158
+ - 0
159
+ - 0
160
+ version: "0.0"
161
+ type: :runtime
162
+ version_requirements: *id009
163
+ - !ruby/object:Gem::Dependency
164
+ name: push-notify
165
+ prerelease: false
166
+ requirement: &id010 !ruby/object:Gem::Requirement
153
167
  none: false
154
168
  requirements:
155
169
  - - ">="
@@ -161,11 +175,11 @@ dependencies:
161
175
  - 0
162
176
  version: 0.1.0
163
177
  type: :runtime
164
- version_requirements: *id009
178
+ version_requirements: *id010
165
179
  - !ruby/object:Gem::Dependency
166
180
  name: rack
167
181
  prerelease: false
168
- requirement: &id010 !ruby/object:Gem::Requirement
182
+ requirement: &id011 !ruby/object:Gem::Requirement
169
183
  none: false
170
184
  requirements:
171
185
  - - ">="
@@ -177,11 +191,11 @@ dependencies:
177
191
  - 0
178
192
  version: 1.0.0
179
193
  type: :runtime
180
- version_requirements: *id010
194
+ version_requirements: *id011
181
195
  - !ruby/object:Gem::Dependency
182
196
  name: rack-cache
183
197
  prerelease: false
184
- requirement: &id011 !ruby/object:Gem::Requirement
198
+ requirement: &id012 !ruby/object:Gem::Requirement
185
199
  none: false
186
200
  requirements:
187
201
  - - ">="
@@ -193,11 +207,11 @@ dependencies:
193
207
  - 2
194
208
  version: 0.5.2
195
209
  type: :runtime
196
- version_requirements: *id011
210
+ version_requirements: *id012
197
211
  - !ruby/object:Gem::Dependency
198
212
  name: rack-rewrite
199
213
  prerelease: false
200
- requirement: &id012 !ruby/object:Gem::Requirement
214
+ requirement: &id013 !ruby/object:Gem::Requirement
201
215
  none: false
202
216
  requirements:
203
217
  - - ">="
@@ -209,11 +223,11 @@ dependencies:
209
223
  - 2
210
224
  version: 1.0.2
211
225
  type: :runtime
212
- version_requirements: *id012
226
+ version_requirements: *id013
213
227
  - !ruby/object:Gem::Dependency
214
228
  name: rack-throttle
215
229
  prerelease: false
216
- requirement: &id013 !ruby/object:Gem::Requirement
230
+ requirement: &id014 !ruby/object:Gem::Requirement
217
231
  none: false
218
232
  requirements:
219
233
  - - ">="
@@ -225,11 +239,11 @@ dependencies:
225
239
  - 0
226
240
  version: 0.3.0
227
241
  type: :runtime
228
- version_requirements: *id013
242
+ version_requirements: *id014
229
243
  - !ruby/object:Gem::Dependency
230
244
  name: rmagick
231
245
  prerelease: false
232
- requirement: &id014 !ruby/object:Gem::Requirement
246
+ requirement: &id015 !ruby/object:Gem::Requirement
233
247
  none: false
234
248
  requirements:
235
249
  - - ">="
@@ -241,11 +255,11 @@ dependencies:
241
255
  - 14
242
256
  version: 1.15.14
243
257
  type: :runtime
244
- version_requirements: *id014
258
+ version_requirements: *id015
245
259
  - !ruby/object:Gem::Dependency
246
260
  name: sqlite3-ruby
247
261
  prerelease: false
248
- requirement: &id015 !ruby/object:Gem::Requirement
262
+ requirement: &id016 !ruby/object:Gem::Requirement
249
263
  none: false
250
264
  requirements:
251
265
  - - ">="
@@ -257,11 +271,11 @@ dependencies:
257
271
  - 1
258
272
  version: 1.2.1
259
273
  type: :runtime
260
- version_requirements: *id015
274
+ version_requirements: *id016
261
275
  - !ruby/object:Gem::Dependency
262
276
  name: tinyatom
263
277
  prerelease: false
264
- requirement: &id016 !ruby/object:Gem::Requirement
278
+ requirement: &id017 !ruby/object:Gem::Requirement
265
279
  none: false
266
280
  requirements:
267
281
  - - ">="
@@ -273,11 +287,11 @@ dependencies:
273
287
  - 3
274
288
  version: 0.3.3
275
289
  type: :runtime
276
- version_requirements: *id016
290
+ version_requirements: *id017
277
291
  - !ruby/object:Gem::Dependency
278
292
  name: twitter
279
293
  prerelease: false
280
- requirement: &id017 !ruby/object:Gem::Requirement
294
+ requirement: &id018 !ruby/object:Gem::Requirement
281
295
  none: false
282
296
  requirements:
283
297
  - - ">="
@@ -289,11 +303,11 @@ dependencies:
289
303
  - 12
290
304
  version: 0.9.12
291
305
  type: :runtime
292
- version_requirements: *id017
306
+ version_requirements: *id018
293
307
  - !ruby/object:Gem::Dependency
294
308
  name: vimeo
295
309
  prerelease: false
296
- requirement: &id018 !ruby/object:Gem::Requirement
310
+ requirement: &id019 !ruby/object:Gem::Requirement
297
311
  none: false
298
312
  requirements:
299
313
  - - ">="
@@ -305,11 +319,11 @@ dependencies:
305
319
  - 2
306
320
  version: 1.2.2
307
321
  type: :runtime
308
- version_requirements: *id018
322
+ version_requirements: *id019
309
323
  - !ruby/object:Gem::Dependency
310
324
  name: flog
311
325
  prerelease: false
312
- requirement: &id019 !ruby/object:Gem::Requirement
326
+ requirement: &id020 !ruby/object:Gem::Requirement
313
327
  none: false
314
328
  requirements:
315
329
  - - ">="
@@ -321,11 +335,11 @@ dependencies:
321
335
  - 0
322
336
  version: 2.5.0
323
337
  type: :development
324
- version_requirements: *id019
338
+ version_requirements: *id020
325
339
  - !ruby/object:Gem::Dependency
326
340
  name: rspec
327
341
  prerelease: false
328
- requirement: &id020 !ruby/object:Gem::Requirement
342
+ requirement: &id021 !ruby/object:Gem::Requirement
329
343
  none: false
330
344
  requirements:
331
345
  - - ~>
@@ -336,7 +350,7 @@ dependencies:
336
350
  - 3
337
351
  version: "1.3"
338
352
  type: :development
339
- version_requirements: *id020
353
+ version_requirements: *id021
340
354
  description: url sharing site framework with easy adding, title lookup, atom feed, thumbnails and embedding
341
355
  email: matthewm@boedicker.org
342
356
  executables:
@@ -356,6 +370,7 @@ files:
356
370
  - config.yaml
357
371
  - lib/murlsh.rb
358
372
  - lib/murlsh/auth.rb
373
+ - lib/murlsh/build_query.rb
359
374
  - lib/murlsh/config_server.rb
360
375
  - lib/murlsh/dispatch.rb
361
376
  - lib/murlsh/doc.rb
@@ -371,8 +386,9 @@ files:
371
386
  - lib/murlsh/plugin.rb
372
387
  - lib/murlsh/sqlite3_adapter.rb
373
388
  - lib/murlsh/time_ago.rb
374
- - lib/murlsh/uri.rb
375
389
  - lib/murlsh/uri_ask.rb
390
+ - lib/murlsh/uri_domain.rb
391
+ - lib/murlsh/uri_get_path_query.rb
376
392
  - lib/murlsh/url.rb
377
393
  - lib/murlsh/url_body.rb
378
394
  - lib/murlsh/url_server.rb
@@ -384,6 +400,8 @@ files:
384
400
  - plugins/add_post_60_notify_hubs.rb
385
401
  - plugins/add_pre_40_convert_mobile.rb
386
402
  - plugins/add_pre_50_lookup_content_type_title.rb
403
+ - plugins/add_pre_50_media_thumbnail.rb
404
+ - plugins/add_pre_50_open_graph_image.rb
387
405
  - plugins/add_pre_60_flickr.rb
388
406
  - plugins/add_pre_60_github_title.rb
389
407
  - plugins/add_pre_60_google_code_title.rb
@@ -391,10 +409,10 @@ files:
391
409
  - plugins/add_pre_60_s3_image.rb
392
410
  - plugins/add_pre_60_twitter.rb
393
411
  - plugins/add_pre_60_vimeo.rb
394
- - plugins/add_pre_60_youtube.rb
395
412
  - plugins/add_pre_65_html_thumb.rb
396
413
  - plugins/add_pre_65_img_thumb.rb
397
- - plugins/html_parse_50_hpricot.rb
414
+ - plugins/avatar_50_gravatar.rb
415
+ - plugins/html_parse_50_nokogiri.rb
398
416
  - plugins/url_display_add_45_audio.rb
399
417
  - plugins/url_display_add_50_hostrec.rb
400
418
  - plugins/url_display_add_55_content_type.rb
@@ -414,7 +432,7 @@ files:
414
432
  - spec/img_store_spec.rb
415
433
  - spec/markup_spec.rb
416
434
  - spec/uri_ask_spec.rb
417
- - spec/uri_spec.rb
435
+ - spec/uri_domain_spec.rb
418
436
  - spec/url_spec.rb
419
437
  - spec/yaml_ordered_hash_spec.rb
420
438
  has_rdoc: true
@@ -458,6 +476,6 @@ test_files:
458
476
  - spec/img_store_spec.rb
459
477
  - spec/markup_spec.rb
460
478
  - spec/uri_ask_spec.rb
461
- - spec/uri_spec.rb
479
+ - spec/uri_domain_spec.rb
462
480
  - spec/url_spec.rb
463
481
  - spec/yaml_ordered_hash_spec.rb
data/lib/murlsh/uri.rb DELETED
@@ -1,16 +0,0 @@
1
- require 'uri'
2
-
3
- # Extra methods added to URI class.
4
- class URI::Generic
5
-
6
- # Return the domain.
7
- def domain
8
- if (host and (d = host[/[a-z\d-]+\.[a-z]{2,}(\.[a-z]{2})?$/]))
9
- d.downcase
10
- end
11
- end
12
-
13
- # Return the path and query string.
14
- def path_query; path + (query ? "?#{query}" : ''); end
15
-
16
- end
@@ -1,32 +0,0 @@
1
- require 'cgi'
2
-
3
- require 'murlsh'
4
-
5
- module Murlsh
6
-
7
- # Set the thumbnail url for youtube urls.
8
- class AddPre60Youtube < Plugin
9
-
10
- @hook = 'add_pre'
11
-
12
- YoutubeRe =
13
- %r{^http://(?:(?:www|uk)\.)?youtube\.com/watch\?v=([\w\-]+)(?:&|$)}i
14
- StorageDir = File.join(File.dirname(__FILE__), '..', 'public', 'img',
15
- 'thumb')
16
-
17
- def self.run(url, config)
18
- if youtube_id = url.url[YoutubeRe, 1]
19
- thumb_storage = Murlsh::ImgStore.new(StorageDir,
20
- :user_agent => config['user_agent'])
21
- stored_filename = thumb_storage.store_url(
22
- "http://img.youtube.com/vi/#{youtube_id}/default.jpg") do |i|
23
- max_side = config.fetch('thumbnail_max_side', 90)
24
- i.extend(Murlsh::ImageList).resize_down!(max_side)
25
- end
26
- url.thumbnail_url = "img/thumb/#{CGI.escape(stored_filename)}"
27
- end
28
- end
29
-
30
- end
31
-
32
- end
@@ -1,19 +0,0 @@
1
- require 'hpricot'
2
-
3
- require 'murlsh'
4
-
5
- module Murlsh
6
-
7
- # Parse HTML with Hpricot and return an Hpricot doc.
8
- class HtmlParse50Hpricot < Plugin
9
-
10
- @hook = 'html_parse'
11
-
12
- def self.run(x)
13
- Hpricot(x)
14
- # Nokogiri(x) also works.
15
- end
16
-
17
- end
18
-
19
- end
data/spec/uri_spec.rb DELETED
@@ -1,14 +0,0 @@
1
- require 'murlsh'
2
-
3
- describe URI do
4
-
5
- it 'should have its domain set to the domain of its URI if it is a valid HTTP URI' do
6
- URI('http://foo.com/').domain.should == 'foo.com'
7
- end
8
-
9
- it 'should have its domain set nil if it is not a valid HTTP URI' do
10
- URI('foo').domain.should be_nil
11
- URI('http://foo.com.').domain.should be_nil
12
- end
13
-
14
- end