quora_notify 0.0.7 → 0.0.9
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.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +2 -0
- data/Rakefile +4 -1
- data/lib/quora_notify.rb +3 -0
- data/lib/quora_notify/base.rb +16 -9
- data/lib/quora_notify/cookie.rb +15 -0
- data/lib/quora_notify/cookie_readers.rb +28 -0
- data/lib/quora_notify/cookie_readers/chrome_cookie_reader.rb +25 -46
- data/lib/quora_notify/cookie_readers/cookie_reader.rb +32 -0
- data/lib/quora_notify/cookie_readers/firefox_cookie_reader.rb +36 -0
- data/lib/quora_notify/cookie_readers/safari_cookie_reader.rb +44 -84
- data/lib/quora_notify/exceptions.rb +4 -0
- data/lib/quora_notify/install.rb +3 -1
- data/lib/quora_notify/version.rb +1 -1
- data/test/test_helper.rb +49 -0
- data/test/test_quora_notify.rb +1 -32
- metadata +22 -18
- data/lib/quora_notify/cookie_readers/cookie.rb +0 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 18a733e9ecd69603d65c8446b7b2ed93d8f37c69
|
4
|
+
data.tar.gz: ffc967ed622f81bc98351421ee28b03bc9d39952
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 630b7abc59ea8d58b0430aadc07250c8a9344e878602be73c00fc927edfa14bcd4b8fbc157ab6503f987d57e62b3c34a9c4288b06005cf6f0d7e038172a2f058
|
7
|
+
data.tar.gz: 76a5838745afafec15c41849a1e8b4e7db59c0d121b9805060ce43c2b747f56f8c3f0b2a043734629bc4ff80bff73406a2bcec403aefe27afebe629940be7ea8
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
Receive quora.com notifications in Notification Center on OSX Mountain Lion.
|
4
4
|
|
5
|
+
Due to the hackish way this currently works (using local cookies), only certain versions of Safari, Chrome, and Firefox are supported for now.
|
6
|
+
|
5
7
|
## Installation
|
6
8
|
|
7
9
|
Install a launchAgent to poll Quora for notifications
|
data/Rakefile
CHANGED
data/lib/quora_notify.rb
CHANGED
data/lib/quora_notify/base.rb
CHANGED
@@ -3,17 +3,14 @@ require 'json'
|
|
3
3
|
require 'terminal-notifier'
|
4
4
|
|
5
5
|
module QuoraNotify
|
6
|
-
|
7
6
|
API_NOTIFICATION_PATH = 'http://api.quora.com/api/logged_in_user?fields=notifs'
|
8
7
|
TMP_COOKIE_PATH = '/tmp/quora_notifier'
|
9
8
|
|
10
9
|
class << self
|
11
10
|
def quora_notifications
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
notification_response = `curl #{API_NOTIFICATION_PATH} -b #{TMP_COOKIE_PATH}`.sub('while(1);', '')
|
16
|
-
JSON.parse(notification_response)
|
11
|
+
fetch_notifications_with_cookies quora_cookies do |notifications|
|
12
|
+
JSON.parse(notifications)
|
13
|
+
end
|
17
14
|
end
|
18
15
|
|
19
16
|
def run!
|
@@ -27,6 +24,13 @@ module QuoraNotify
|
|
27
24
|
|
28
25
|
private
|
29
26
|
|
27
|
+
def fetch_notifications_with_cookies(cookies)
|
28
|
+
write_cookies_to_file(cookies, TMP_COOKIE_PATH)
|
29
|
+
response = `curl #{API_NOTIFICATION_PATH} -b #{TMP_COOKIE_PATH}`.sub('while(1);', '')
|
30
|
+
File.delete TMP_COOKIE_PATH
|
31
|
+
yield response
|
32
|
+
end
|
33
|
+
|
30
34
|
def process(notifications)
|
31
35
|
return [] if notifications.empty? || notifications['notifs']['unseen_count'] == 0
|
32
36
|
|
@@ -36,17 +40,18 @@ module QuoraNotify
|
|
36
40
|
end
|
37
41
|
|
38
42
|
def write_cookies_to_file(cookies, file)
|
39
|
-
File.open(file, 'w') do |
|
43
|
+
File.open(file, 'w') do |f|
|
40
44
|
cookies.each do |cookie|
|
41
45
|
# TODO: could be in to_s for cookie object
|
42
|
-
|
46
|
+
f.puts "#{cookie.domain}\tTRUE\t#{cookie.path}\tFALSE\t#{cookie.expires}\t#{cookie.name}\t#{cookie.value}"
|
43
47
|
end
|
44
48
|
end
|
45
49
|
end
|
46
50
|
|
47
|
-
def
|
51
|
+
def quora_cookies
|
48
52
|
readers = [
|
49
53
|
CookieReaders::ChromeCookieReader.new,
|
54
|
+
CookieReaders::FirefoxCookieReader.new,
|
50
55
|
CookieReaders::SafariCookieReader.new
|
51
56
|
]
|
52
57
|
|
@@ -54,6 +59,8 @@ module QuoraNotify
|
|
54
59
|
begin
|
55
60
|
reader.read_cookies
|
56
61
|
rescue => e
|
62
|
+
puts e.message
|
63
|
+
puts e.backtrace
|
57
64
|
[]
|
58
65
|
end
|
59
66
|
end.flatten.uniq.select { |cookie| cookie.domain == '.quora.com' }
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module QuoraNotify
|
2
|
+
class Cookie
|
3
|
+
attr_reader :name, :value, :domain, :path, :flag, :expires, :created
|
4
|
+
|
5
|
+
def initialize(opts = {})
|
6
|
+
@name = opts[:name]
|
7
|
+
@value = opts[:value]
|
8
|
+
@domain = opts[:domain]
|
9
|
+
@path = opts[:path]
|
10
|
+
@flag = opts[:flag]
|
11
|
+
@expires = opts[:expires]
|
12
|
+
@created = opts[:created]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -1,5 +1,33 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
|
1
3
|
module QuoraNotify::CookieReaders
|
4
|
+
class CookieIO < StringIO
|
5
|
+
def next
|
6
|
+
self.read(4)
|
7
|
+
end
|
8
|
+
|
9
|
+
def next_as_int
|
10
|
+
self.next.unpack('l<')[0]
|
11
|
+
end
|
12
|
+
|
13
|
+
def read_as_flag
|
14
|
+
case self.next_as_int
|
15
|
+
when 0; ''
|
16
|
+
when 1; 'Secure'
|
17
|
+
when 4; 'HttpOnly'
|
18
|
+
when 5; 'Secure: HttpOnly'
|
19
|
+
else; 'Unknown'
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def read_as_date
|
24
|
+
epoch = self.read(8).unpack('E')[0] + 978307200
|
25
|
+
Time.at(epoch).strftime('%a, %d %b %Y')
|
26
|
+
end
|
27
|
+
end
|
2
28
|
end
|
3
29
|
|
30
|
+
require 'quora_notify/cookie_readers/cookie_reader'
|
31
|
+
require 'quora_notify/cookie_readers/firefox_cookie_reader'
|
4
32
|
require 'quora_notify/cookie_readers/safari_cookie_reader'
|
5
33
|
require 'quora_notify/cookie_readers/chrome_cookie_reader'
|
@@ -1,56 +1,35 @@
|
|
1
1
|
require 'sqlite3'
|
2
|
-
require 'quora_notify/cookie_readers/cookie'
|
3
2
|
|
4
|
-
|
3
|
+
module QuoraNotify::CookieReaders
|
4
|
+
class ChromeCookieReader < CookieReader
|
5
|
+
DEFAULT_COOKIE_PATH = "#{ENV['HOME']}/Library/Application Support/Google/Chrome/Default/Cookies"
|
5
6
|
|
6
|
-
|
7
|
+
# Returns an array of Cookie objects
|
8
|
+
# Raises: binarycookieReaderException
|
9
|
+
def read_cookies
|
10
|
+
raise QuoraNotify::Exceptions::CookieReaderException, 'No cookie file found' unless File.exists?(cookie_file_path)
|
7
11
|
|
8
|
-
|
12
|
+
cookies = []
|
13
|
+
db = SQLite3::Database.new(cookie_file_path)
|
14
|
+
db.execute('SELECT name, value, host_key, path, secure, httponly, expires_utc, creation_utc FROM cookies') do |row|
|
15
|
+
cookies << cookie_for_row(row)
|
16
|
+
end
|
9
17
|
|
10
|
-
|
11
|
-
# cookie_file_path: Use default cookie file if nil (default)
|
12
|
-
def initialize(file_path = nil)
|
13
|
-
@cookie_file_path = file_path
|
14
|
-
end
|
15
|
-
|
16
|
-
# Returns an array of Cookie objects
|
17
|
-
# Raises: binarycookieReaderException
|
18
|
-
def read_cookies
|
19
|
-
raise ChromeCookieReaderException 'No cookie file found' unless File.exists?(cookie_file_path)
|
20
|
-
|
21
|
-
cookies = []
|
22
|
-
db = SQLite3::Database.new(cookie_file_path)
|
23
|
-
db.execute('SELECT name, value, host_key, path, secure, httponly, expires_utc, creation_utc FROM cookies') do |row|
|
24
|
-
cookies << cookie_for_row(row)
|
18
|
+
cookies
|
25
19
|
end
|
26
20
|
|
27
|
-
|
28
|
-
end
|
29
|
-
|
30
|
-
private
|
21
|
+
private
|
31
22
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
created = row[7]
|
23
|
+
def cookie_for_row(row)
|
24
|
+
name = row[0]
|
25
|
+
value = row[1]
|
26
|
+
domain = row[2]
|
27
|
+
path = row[3]
|
28
|
+
flag = flag(row[4], row[5])
|
29
|
+
expires = row[6]
|
30
|
+
created = row[7]
|
41
31
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
def flag(secure, httponly)
|
46
|
-
return 'Secure: HttpOnly' if secure == 1 && httponly == 1
|
47
|
-
return 'Secure' if secure == 1
|
48
|
-
return 'HttpOnly' if httponly == 1
|
49
|
-
'Unknown'
|
50
|
-
end
|
51
|
-
|
52
|
-
# Defaults to the standard safari cookie system path
|
53
|
-
def cookie_file_path
|
54
|
-
@cookie_file_path || DEFAULT_CHROME_COOKIE_PATH
|
55
|
-
end
|
32
|
+
QuoraNotify::Cookie.new(name: name, value: value, domain: domain, path: path, flag: flag, expires: expires, created: created)
|
33
|
+
end
|
34
|
+
end
|
56
35
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module QuoraNotify::CookieReaders
|
2
|
+
# Subclasses should define a DEFAULT_COOKIE_PATH constant
|
3
|
+
# that will be used if no file path is provided to the constructor
|
4
|
+
class CookieReader
|
5
|
+
DEFAULT_COOKIE_PATH = nil
|
6
|
+
# Accepts:
|
7
|
+
# cookie_file_path: Use DEFAULT_COOKIE_PATH if nil (default)
|
8
|
+
def initialize(file_path = nil)
|
9
|
+
@cookie_file_path = file_path
|
10
|
+
end
|
11
|
+
|
12
|
+
# Returns an array of Cookie objects
|
13
|
+
# Raises: binarycookieReaderException
|
14
|
+
def read_cookies
|
15
|
+
raise QuoraNotify::Exceptions::CookieReaderException, 'Not implemented'
|
16
|
+
end
|
17
|
+
|
18
|
+
# Defaults to the standard safari cookie system path
|
19
|
+
def cookie_file_path
|
20
|
+
@cookie_file_path || self.class::DEFAULT_COOKIE_PATH
|
21
|
+
end
|
22
|
+
|
23
|
+
protected
|
24
|
+
|
25
|
+
def flag(secure, httponly)
|
26
|
+
return 'Secure: HttpOnly' if secure == 1 && httponly == 1
|
27
|
+
return 'Secure' if secure == 1
|
28
|
+
return 'HttpOnly' if httponly == 1
|
29
|
+
'Unknown'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'sqlite3'
|
2
|
+
|
3
|
+
module QuoraNotify::CookieReaders
|
4
|
+
class FirefoxCookieReader < CookieReader
|
5
|
+
DEFAULT_COOKIE_PATH = "#{ENV['HOME']}/Library/Application Support/Firefox/Profiles/*/cookies.sqlite"
|
6
|
+
|
7
|
+
# Returns an array of Cookie objects
|
8
|
+
# Raises: binarycookieReaderException
|
9
|
+
def read_cookies
|
10
|
+
cookie_file = Dir[cookie_file_path][0]
|
11
|
+
raise QuoraNotify::Exceptions::CookieReaderException, 'No cookie file found' unless cookie_file && File.exists?(cookie_file)
|
12
|
+
|
13
|
+
cookies = []
|
14
|
+
db = SQLite3::Database.new(cookie_file)
|
15
|
+
db.execute('SELECT name, value, baseDomain, path, isSecure, isHttpOnly, expiry, creationTime FROM moz_cookies') do |row|
|
16
|
+
cookies << cookie_for_row(row)
|
17
|
+
end
|
18
|
+
|
19
|
+
cookies
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def cookie_for_row(row)
|
25
|
+
name = row[0]
|
26
|
+
value = row[1]
|
27
|
+
domain = row[2]
|
28
|
+
path = row[3]
|
29
|
+
flag = flag(row[4], row[5])
|
30
|
+
expires = row[6]
|
31
|
+
created = row[7]
|
32
|
+
|
33
|
+
QuoraNotify::Cookie.new(name: name, value: value, domain: domain, path: path, flag: flag, expires: expires, created: created)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -1,107 +1,67 @@
|
|
1
|
+
module QuoraNotify::CookieReaders
|
2
|
+
class SafariCookieReader < CookieReader
|
3
|
+
DEFAULT_COOKIE_PATH = "#{ENV['HOME']}/Library/Cookies/Cookies.binarycookies"
|
1
4
|
|
2
|
-
|
3
|
-
|
5
|
+
# Returns an array of Cookie objects
|
6
|
+
# Raises: CookieReaderException
|
7
|
+
def read_cookies
|
8
|
+
raise QuoraNotify::Exceptions::CookieReaderException, 'No cookie file found' unless File.exists?(cookie_file_path)
|
4
9
|
|
5
|
-
|
6
|
-
|
7
|
-
class QuoraNotify::CookieReaders::SafariCookieReader
|
8
|
-
|
9
|
-
class CookieIO < StringIO
|
10
|
-
def next
|
11
|
-
self.read(4)
|
12
|
-
end
|
13
|
-
|
14
|
-
def next_as_int
|
15
|
-
self.next.unpack('l<')[0]
|
16
|
-
end
|
17
|
-
|
18
|
-
def read_as_flag
|
19
|
-
case self.next_as_int
|
20
|
-
when 0; ''
|
21
|
-
when 1; 'Secure'
|
22
|
-
when 4; 'HttpOnly'
|
23
|
-
when 5; 'Secure: HttpOnly'
|
24
|
-
else; 'Unknown'
|
10
|
+
File.open(cookie_file_path, 'rb') do |cookie_file|
|
11
|
+
process_file cookie_file
|
25
12
|
end
|
26
13
|
end
|
27
14
|
|
28
|
-
|
29
|
-
epoch = self.read(8).unpack('E')[0] + 978307200
|
30
|
-
Time.at(epoch).strftime('%a, %d %b %Y')
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
DEFAULT_SAFARI_COOKIE_PATH = "#{ENV['HOME']}/Library/Cookies/Cookies.binarycookies"
|
15
|
+
private
|
35
16
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
@cookie_file_path = file_path
|
40
|
-
end
|
17
|
+
def process_file(cookie_file)
|
18
|
+
header = cookie_file.read(4)
|
19
|
+
raise QuoraNotify::Exceptions::CookieReaderException, 'Not recognized as a binarycookie file' unless header == 'cook'
|
41
20
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
raise SafariCookieReaderException 'No cookie file found' unless File.exists?(cookie_file_path)
|
21
|
+
n_pages = cookie_file.read(4).unpack('l>')[0]
|
22
|
+
sizes = (0..n_pages - 1).collect { |page| cookie_file.read(4).unpack('l>')[0] }
|
23
|
+
pages = sizes.collect { |size| cookie_file.read(size) }
|
46
24
|
|
47
|
-
|
48
|
-
|
49
|
-
end
|
50
|
-
end
|
25
|
+
cookie_groups = pages.collect do |page|
|
26
|
+
page = CookieIO.new(page); page.next
|
51
27
|
|
52
|
-
|
53
|
-
|
54
|
-
raise SafariCookieReaderException 'Not recognized as a binarycookie file' unless header == 'cook'
|
28
|
+
n_items = page.next_as_int
|
29
|
+
cookie_offsets = (0..n_items - 1).collect { |item| page.next_as_int }
|
55
30
|
|
56
|
-
|
57
|
-
sizes = (0..n_pages - 1).collect { |page| cookie_file.read(4).unpack('l>')[0] }
|
58
|
-
pages = sizes.collect { |size| cookie_file.read(size) }
|
31
|
+
page.next
|
59
32
|
|
60
|
-
|
61
|
-
|
33
|
+
cookie_offsets.collect do |offset|
|
34
|
+
page.seek(offset) # Move to the start of the next page of cookies
|
35
|
+
cookie_for_data CookieIO.new(page.read(page.next_as_int)) # Pulls each cookie out of the page
|
36
|
+
end
|
37
|
+
end
|
62
38
|
|
63
|
-
|
64
|
-
cookie_offsets = (0..n_items - 1).collect { |item| page.next_as_int }
|
65
|
-
|
66
|
-
page.next
|
67
|
-
|
68
|
-
cookie_offsets.collect do |offset|
|
69
|
-
page.seek(offset) # Move to the start of the next page of cookies
|
70
|
-
cookie_for_data CookieIO.new(page.read(page.next_as_int)) # Pulls each cookie out of the page
|
39
|
+
cookie_groups.flatten
|
71
40
|
end
|
72
|
-
end
|
73
41
|
|
74
|
-
|
75
|
-
|
42
|
+
def cookie_for_data(cookie)
|
43
|
+
cookie.next; flag = cookie.read_as_flag; cookie.next
|
76
44
|
|
77
|
-
|
78
|
-
cookie.next; flag = cookie.read_as_flag; cookie.next
|
45
|
+
domain_off, name_off, path_off, value_off = 4.times.map { cookie.next_as_int }
|
79
46
|
|
80
|
-
|
47
|
+
cookie.next; cookie.next
|
81
48
|
|
82
|
-
|
49
|
+
expires, created = 2.times.map { cookie.read_as_date }
|
83
50
|
|
84
|
-
|
51
|
+
domain, name, path, value = [domain_off, name_off, path_off, value_off].collect do |offset|
|
52
|
+
cookie.seek(offset - 4)
|
85
53
|
|
86
|
-
|
87
|
-
|
54
|
+
data = ''
|
55
|
+
while char = cookie.read(1)
|
56
|
+
new_data = char.unpack('c')[0]
|
57
|
+
break if new_data == 0
|
58
|
+
data << new_data
|
59
|
+
end
|
88
60
|
|
89
|
-
|
90
|
-
|
91
|
-
new_data = char.unpack('c')[0]
|
92
|
-
break if new_data == 0
|
93
|
-
data << new_data
|
94
|
-
end
|
61
|
+
data
|
62
|
+
end
|
95
63
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
QuoraNotify::CookieReaders::Cookie.new(name: name, value: value, domain: domain, path: path, flag: flag, expires: expires, created: created)
|
100
|
-
end
|
101
|
-
|
102
|
-
# Defaults to the standard safari cookie system path
|
103
|
-
def cookie_file_path
|
104
|
-
@cookie_file_path || DEFAULT_SAFARI_COOKIE_PATH
|
64
|
+
QuoraNotify::Cookie.new(name: name, value: value, domain: domain, path: path, flag: flag, expires: expires, created: created)
|
65
|
+
end
|
105
66
|
end
|
106
|
-
|
107
67
|
end
|
data/lib/quora_notify/install.rb
CHANGED
@@ -2,7 +2,6 @@ require "fileutils"
|
|
2
2
|
require 'erb'
|
3
3
|
|
4
4
|
class QuoraNotify::Install
|
5
|
-
|
6
5
|
INSTALL_PATH = File.join(ENV["HOME"], "Library", "LaunchAgents")
|
7
6
|
LAUNCHD_PROCESS_NAME = "com.joeycarmello.quora_notify"
|
8
7
|
LAUNCHD_FILE_NAME = "#{LAUNCHD_PROCESS_NAME}.plist"
|
@@ -11,9 +10,12 @@ class QuoraNotify::Install
|
|
11
10
|
class << self
|
12
11
|
def run!
|
13
12
|
uninstall!
|
13
|
+
|
14
|
+
# Used with the ERB binding
|
14
15
|
gem_path = ENV['GEM_HOME']
|
15
16
|
ruby_path = File.join(RbConfig::CONFIG['bindir'], RbConfig::CONFIG['ruby_install_name']).sub(/.*\s.*/m, '"\&"')
|
16
17
|
bin_path = `which quora_notify`.chomp
|
18
|
+
|
17
19
|
template = ERB.new File.read(erb_path)
|
18
20
|
File.write(install_file_path, template.result(binding))
|
19
21
|
start
|
data/lib/quora_notify/version.rb
CHANGED
data/test/test_helper.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'quora_notify'
|
3
|
+
require 'mocha/setup'
|
4
|
+
require 'fileutils'
|
5
|
+
|
6
|
+
def fill_with_mocks
|
7
|
+
# create some notifications
|
8
|
+
QuoraNotify.expects(:quora_notifications).returns(mock_notifications)
|
9
|
+
# go fetch for the fake ones, creating real local notifs
|
10
|
+
QuoraNotify.run!
|
11
|
+
end
|
12
|
+
|
13
|
+
def mock_notifications
|
14
|
+
{
|
15
|
+
'notifs' => {
|
16
|
+
'unseen_count' => 2,
|
17
|
+
'unseen' => [
|
18
|
+
'<a href="http://quora.com">Some text</a>',
|
19
|
+
'<a href="http://quora.com">Some text 2</a>'
|
20
|
+
]
|
21
|
+
}
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
def mock_empty_notifications
|
26
|
+
{
|
27
|
+
'notifs' => {
|
28
|
+
'unseen_count' => 0,
|
29
|
+
'unseen' => []
|
30
|
+
}
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
def make_tmp_file_for_reference
|
35
|
+
tmp_path = '/tmp/some_file_that_will_be_deleted'
|
36
|
+
FileUtils.touch(tmp_path)
|
37
|
+
yield tmp_path if block_given?
|
38
|
+
File.delete tmp_path
|
39
|
+
end
|
40
|
+
|
41
|
+
class MockSqlite3DBResults
|
42
|
+
NUMBER_OF_COOKIES = 3
|
43
|
+
|
44
|
+
def execute(query)
|
45
|
+
result = NUMBER_OF_COOKIES.times.collect { ['name', 'value', 'host', 'path', 1, 0, Time.now.to_i, Time.now.to_i] }
|
46
|
+
result.each { |row| yield row } if block_given?
|
47
|
+
result
|
48
|
+
end
|
49
|
+
end
|
data/test/test_quora_notify.rb
CHANGED
@@ -1,6 +1,4 @@
|
|
1
|
-
require '
|
2
|
-
require 'mocha/setup'
|
3
|
-
require 'quora_notify'
|
1
|
+
require 'test_helper'
|
4
2
|
|
5
3
|
class QuoraNotifyTest < Test::Unit::TestCase
|
6
4
|
def test_can_parse_quora_api_response
|
@@ -37,33 +35,4 @@ class QuoraNotifyTest < Test::Unit::TestCase
|
|
37
35
|
fill_with_mocks
|
38
36
|
end
|
39
37
|
|
40
|
-
private
|
41
|
-
|
42
|
-
def fill_with_mocks
|
43
|
-
# create some notifications
|
44
|
-
QuoraNotify.expects(:quora_notifications).returns(mock_notifications)
|
45
|
-
# go fetch for the fake ones, creating real local notifs
|
46
|
-
QuoraNotify.run!
|
47
|
-
end
|
48
|
-
|
49
|
-
def mock_notifications
|
50
|
-
{
|
51
|
-
'notifs' => {
|
52
|
-
'unseen_count' => 2,
|
53
|
-
'unseen' => [
|
54
|
-
'<a href="http://quora.com">Some text</a>',
|
55
|
-
'<a href="http://quora.com">Some text 2</a>'
|
56
|
-
]
|
57
|
-
}
|
58
|
-
}
|
59
|
-
end
|
60
|
-
|
61
|
-
def mock_empty_notifications
|
62
|
-
{
|
63
|
-
'notifs' => {
|
64
|
-
'unseen_count' => 0,
|
65
|
-
'unseen' => []
|
66
|
-
}
|
67
|
-
}
|
68
|
-
end
|
69
38
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: quora_notify
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joey Carmello
|
@@ -14,58 +14,58 @@ dependencies:
|
|
14
14
|
name: terminal-notifier
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - ~>
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '1.4'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - ~>
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '1.4'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: sqlite3
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - ~>
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '1.3'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- -
|
38
|
+
- - ~>
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '1.3'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: htmlentities
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- -
|
45
|
+
- - ~>
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
47
|
+
version: '4.3'
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- -
|
52
|
+
- - ~>
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
54
|
+
version: '4.3'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: mocha
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- -
|
59
|
+
- - ~>
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: '0'
|
61
|
+
version: '0.13'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- -
|
66
|
+
- - ~>
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: '0'
|
68
|
+
version: '0.13'
|
69
69
|
description: |2
|
70
70
|
QuoraNotify provides basic access to one of the few API's Quora.com has opened to the public.
|
71
71
|
It uses local quora cookies to access this API as a logged-in user.
|
@@ -78,15 +78,19 @@ extensions: []
|
|
78
78
|
extra_rdoc_files: []
|
79
79
|
files:
|
80
80
|
- lib/quora_notify/base.rb
|
81
|
+
- lib/quora_notify/cookie.rb
|
81
82
|
- lib/quora_notify/cookie_readers/chrome_cookie_reader.rb
|
82
|
-
- lib/quora_notify/cookie_readers/
|
83
|
+
- lib/quora_notify/cookie_readers/cookie_reader.rb
|
84
|
+
- lib/quora_notify/cookie_readers/firefox_cookie_reader.rb
|
83
85
|
- lib/quora_notify/cookie_readers/safari_cookie_reader.rb
|
84
86
|
- lib/quora_notify/cookie_readers.rb
|
87
|
+
- lib/quora_notify/exceptions.rb
|
85
88
|
- lib/quora_notify/install.rb
|
86
89
|
- lib/quora_notify/notification.rb
|
87
90
|
- lib/quora_notify/version.rb
|
88
91
|
- lib/quora_notify.rb
|
89
92
|
- bin/quora_notify
|
93
|
+
- test/test_helper.rb
|
90
94
|
- test/test_quora_notify.rb
|
91
95
|
- Gemfile
|
92
96
|
- Gemfile.lock
|
@@ -1,14 +0,0 @@
|
|
1
|
-
|
2
|
-
class QuoraNotify::CookieReaders::Cookie
|
3
|
-
attr_reader :name, :value, :domain, :path, :flag, :expires, :created
|
4
|
-
|
5
|
-
def initialize(opts = {})
|
6
|
-
@name = opts[:name]
|
7
|
-
@value = opts[:value]
|
8
|
-
@domain = opts[:domain]
|
9
|
-
@path = opts[:path]
|
10
|
-
@flag = opts[:flag]
|
11
|
-
@expires = opts[:expires]
|
12
|
-
@created = opts[:created]
|
13
|
-
end
|
14
|
-
end
|