murlsh 1.1.0 → 1.2.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/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