qiita_picks 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/.rspec +2 -0
- data/.travis.yml +3 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +160 -0
- data/Rakefile +7 -0
- data/bin/qiita_picks +137 -0
- data/lib/qiita_picks.rb +48 -0
- data/lib/qiita_picks/generator.rb +116 -0
- data/lib/qiita_picks/mailer.rb +42 -0
- data/lib/qiita_picks/picker.rb +128 -0
- data/lib/qiita_picks/templates/article.erb +5 -0
- data/lib/qiita_picks/templates/layout.erb +11 -0
- data/lib/qiita_picks/version.rb +3 -0
- data/qiita_picks.gemspec +43 -0
- data/spec/qiita_picks_spec.rb +7 -0
- data/spec/spec_helper.rb +2 -0
- metadata +215 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: dd111977c59c8d4025f8b77947b3819d448837d4
|
4
|
+
data.tar.gz: 11f8e3254e1e48cb600e8342eee4f022747196f6
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e8226211760dcf4392b22710294c0eb62cb6e86cad24345efb8f0bb5c0a2e3dd7004d303ba4afdb34da273a0d290999b89a4cbdf17163e98a09bad8f0ae670a1
|
7
|
+
data.tar.gz: 1b4221573895799dbbaaa24de9ba9269bf29452bafa8d316556aa0abb3a72b3ee61e8b2472857d7c9a079f0502b9adce3909b97e1a89119e2f85f1a180c9ed53
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 @ HentecoLabs Inc
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,160 @@
|
|
1
|
+
# QiitaPicks
|
2
|
+
|
3
|
+
**:fork_and_knife:Qiita[キータ]に投稿された記事を “つまんで” まとめPDFを生成:fork_and_knife:**
|
4
|
+
|
5
|
+
![](https://s3-ap-northeast-1.amazonaws.com/hentecolabs/tty.gif)
|
6
|
+
|
7
|
+
**Generate PDF/HTML** from articles posted on **[qiita.com](http://qiita.com/)**.
|
8
|
+
Uses **[wkhtmltopdf](http://wkhtmltopdf.org/)** on the back-end which renders HTML using Webkit.
|
9
|
+
|
10
|
+
## できること
|
11
|
+
|
12
|
+
#### **まとめPDFをコマンド一発で生成**
|
13
|
+
|
14
|
+
`qiita_picks`コマンドで、次の「まとめPDF」が生成できます。
|
15
|
+
|
16
|
+
1. **指定URL**のまとめPDF
|
17
|
+
1. **特定のユーザが投稿した記事**のまとめPDF
|
18
|
+
1. **特定のユーザがストックした記事**のまとめPDF
|
19
|
+
1. **『先週ストックが多かった投稿ベスト20』メールに掲載された記事**のまとめPDF
|
20
|
+
1. **『今週Qiitaでストックした記事』メールに掲載された記事**のまとめPDF
|
21
|
+
1. **はてなブックマークにブックマークされた記事(人気)**のまとめPDF
|
22
|
+
1. **はてなブックマークにブックマークされた記事(新着)**のまとめPDF
|
23
|
+
|
24
|
+
#### **まとめPDFは、印刷・閲覧に最適化**
|
25
|
+
|
26
|
+
印刷向けにスタイルシートを調整しています。
|
27
|
+
|
28
|
+
#### **まとめPDFは、Gmailで送信可能**
|
29
|
+
|
30
|
+
- Gmailの送受信には「**[ruby-gmail](https://github.com/dcparker/ruby-gmail)**」を利用しています。
|
31
|
+
- メールは送受信共に、利用者自身となります。
|
32
|
+
|
33
|
+
## Installation
|
34
|
+
|
35
|
+
### **QiitaPicks**
|
36
|
+
|
37
|
+
```zsh
|
38
|
+
% gem install qiita_picks
|
39
|
+
```
|
40
|
+
|
41
|
+
### **wkhtmltopdf**
|
42
|
+
|
43
|
+
1. Install by hand (recommended):
|
44
|
+
|
45
|
+
<https://github.com/pdfkit/pdfkit/wiki/Installing-WKHTMLTOPDF>
|
46
|
+
|
47
|
+
2. Try using the `wkhtmltopdf-binary` gem (mac + linux i386)
|
48
|
+
```
|
49
|
+
gem install wkhtmltopdf-binary
|
50
|
+
```
|
51
|
+
*Note:* The automated installer has been removed.
|
52
|
+
|
53
|
+
## Getting Started
|
54
|
+
|
55
|
+
### **BASIC**
|
56
|
+
|
57
|
+
```zsh
|
58
|
+
# 指定したURLの記事をまとめる (カンマ区切り)
|
59
|
+
% qiita_picks <URLs>
|
60
|
+
|
61
|
+
# 特定ユーザーの記事をまとめる
|
62
|
+
% qiita_picks list_user_items -u <username> -a <access_token>
|
63
|
+
|
64
|
+
# 特定ユーザーがストックした記事をまとめる
|
65
|
+
% qiita_picks list_user_stocks -u <username> -a <access_token>
|
66
|
+
|
67
|
+
# 特定のタグに関連する記事をまとめる
|
68
|
+
% qiita_picks list_tag_items -t <tag> -a <access_token>
|
69
|
+
|
70
|
+
# 『先週ストックが多かった投稿ベスト20』メールに掲載された記事をまとめる
|
71
|
+
% qiita_picks email_weekly_popular -e <email> -p <password>
|
72
|
+
|
73
|
+
# 『今週Qiitaでストックした記事』メールに掲載された記事をまとめる
|
74
|
+
% qiita_picks email_weekly_stocked -e <email> -p <password>
|
75
|
+
|
76
|
+
# はてなブックマークにブックマークされた人気の記事をまとめる
|
77
|
+
% qiita_picks list_hatena_popular -t <tag>
|
78
|
+
|
79
|
+
# はてなブックマークにブックマークされた新着の記事をまとめる
|
80
|
+
% qiita_picks list_hatena_recent -t <tag>
|
81
|
+
```
|
82
|
+
|
83
|
+
タグ・ユーザ名での記事の取得時に、Qiita Developer APIを利用しています。
|
84
|
+
事前にアカウントとアクセストークンを取得してください。
|
85
|
+
|
86
|
+
[新しくトークンを発行する](https://qiita.com/settings/tokens/new)
|
87
|
+
|
88
|
+
### **ADVANCED**
|
89
|
+
|
90
|
+
生成したPDFをメールに添付すると、デバイスを選ばずに、
|
91
|
+
いつでもオフラインで学習できるので、プログラマー生活がより快適になります。
|
92
|
+
|
93
|
+
それぞれのコマンドで生成したPDFを送信する場合は、`-e <email>` と `-p <password>` のオプションを設定してください。
|
94
|
+
|
95
|
+
:warning: **メールの送受信は共に、指定したGmailアカウントから実行されます**
|
96
|
+
|
97
|
+
```zsh
|
98
|
+
# 指定したURLの記事をまとめる (カンマ区切り) + メール送信
|
99
|
+
% qiita_picks <URLs> -e <email> -p <password>
|
100
|
+
```
|
101
|
+
|
102
|
+
なお、`email_weekly_popular`と`email_weekly_stocked`コマンドについては、
|
103
|
+
別途、`-s <send_mail_flag>`を渡す必要があります。
|
104
|
+
|
105
|
+
```zsh
|
106
|
+
# 『先週ストックが多かった投稿ベスト20』メールに掲載された記事 + メール送信
|
107
|
+
% qiita_picks email_weekly_popular -e <email> -p <password> -s true
|
108
|
+
```
|
109
|
+
|
110
|
+
特殊な処理がしたい場合は、以下を参考にご自由にどうぞ。
|
111
|
+
|
112
|
+
```ruby
|
113
|
+
require 'qiita_picks'
|
114
|
+
|
115
|
+
url = %w{http://qiita.com/baba_s/items/c8d25456c0ee4ef52f94 http://qiita.com/y_minowa/items/f12aabbae7b0048cfe64}
|
116
|
+
generator = QiitaPicks::Generator.new(email, password, access_token)
|
117
|
+
|
118
|
+
# 指定したURLの記事をまとめる
|
119
|
+
generator.generate_from_url(url)
|
120
|
+
|
121
|
+
# 指定したURLの記事をまとめて 自身のGmailアカウントに送信
|
122
|
+
generator.generate_from_url(url, deliver: true)
|
123
|
+
```
|
124
|
+
|
125
|
+
### **TIPS**
|
126
|
+
|
127
|
+
#### クラウドサービスにアップロード
|
128
|
+
|
129
|
+
ローカルにある、Dropbox等の**クラウドサービスと同期するフォルダに向けて生成**するのが、最もラクかと思います。メールで送りたくない場合は、お試しください。
|
130
|
+
|
131
|
+
#### JenkinsやCronで定期的に自動生成
|
132
|
+
|
133
|
+
`qiita_picks`コマンドを定期的に実行するようスケジューリングすると、インプットが捗ります。下記のリンクを参考に設定してみてはいかがでしょうか。
|
134
|
+
|
135
|
+
- [cronに関する投稿 - Qiita](http://qiita.com/tags/cron)
|
136
|
+
- [OS XにJenkinsをHomebrewでセットアップする - Qiita](http://qiita.com/makoto_kw/items/cbe93d4ebbc35f3b43fd)
|
137
|
+
|
138
|
+
## Caution
|
139
|
+
|
140
|
+
- 作成したPDFを、インターネット上で公開・配布することはご遠慮ください。
|
141
|
+
- Qiita[キータ]へのアクセス負荷を考慮し、**節度のあるご利用**をお願いします。
|
142
|
+
- Qiita[キータ]は、Increments株式会社が提供するサービスです。当スクリプトは、Increments株式会社様による提供ではございませんので、何卒ご注意ください。
|
143
|
+
|
144
|
+
## Feedback
|
145
|
+
|
146
|
+
何かお気づきの点があれば、[こちらのフォーム](http://goo.gl/forms/PGFggwhCu1)よりご意見をお送りください :sunglasses:
|
147
|
+
|
148
|
+
## Contributing
|
149
|
+
|
150
|
+
不具合等ありましたら、お気軽にプルリクしてください。
|
151
|
+
|
152
|
+
1. Fork it ( https://github.com/henteco-labs/qiita_picks/fork )
|
153
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
154
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
155
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
156
|
+
5. Create a new Pull Request
|
157
|
+
|
158
|
+
## Copyright
|
159
|
+
|
160
|
+
Copyright © 2015 HentecoLabs Inc. See LICENSE.txt for further details.
|
data/Rakefile
ADDED
data/bin/qiita_picks
ADDED
@@ -0,0 +1,137 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'rubygems'
|
3
|
+
|
4
|
+
# external dependencies
|
5
|
+
require 'qiita_picks'
|
6
|
+
|
7
|
+
# stdlib dependencies
|
8
|
+
require 'optparse'
|
9
|
+
require 'uri'
|
10
|
+
|
11
|
+
opts = OptionParser.new do |opts|
|
12
|
+
opts.banner = <<-EOF
|
13
|
+
|
14
|
+
QiitaPicks - Qiitaに投稿された記事をまとめて PDFファイルを作成
|
15
|
+
|
16
|
+
EOF
|
17
|
+
opts.separator <<-EOF
|
18
|
+
Examples:
|
19
|
+
|
20
|
+
# 指定したURLの記事をまとめる (カンマ区切り)
|
21
|
+
% qiita_picks <URLs>
|
22
|
+
|
23
|
+
# 特定ユーザーの記事をまとめる
|
24
|
+
% qiita_picks list_user_items -u <username> -a <access_token>
|
25
|
+
|
26
|
+
# 特定ユーザーがストックした記事をまとめる
|
27
|
+
% qiita_picks list_user_stocks -u <username> -a <access_token>
|
28
|
+
|
29
|
+
# 特定のタグに関連する記事をまとめる
|
30
|
+
% qiita_picks list_tag_items -t <tag> -a <access_token>
|
31
|
+
|
32
|
+
# 『先週ストックが多かった投稿ベスト20』メールに掲載された記事をまとめる
|
33
|
+
% qiita_picks email_weekly_popular -e <email> -p <password>
|
34
|
+
|
35
|
+
# 『今週Qiitaでストックした記事』メールに掲載された記事をまとめる
|
36
|
+
% qiita_picks email_weekly_stocked -e <email> -p <password>
|
37
|
+
|
38
|
+
# はてなブックマークにブックマークされた人気の記事をまとめる
|
39
|
+
% qiita_picks list_hatena_popular -t <tag>
|
40
|
+
|
41
|
+
# はてなブックマークにブックマークされた新着の記事をまとめる
|
42
|
+
% qiita_picks list_hatena_recent -t <tag>
|
43
|
+
|
44
|
+
Options:
|
45
|
+
|
46
|
+
EOF
|
47
|
+
|
48
|
+
opts.on("-a", "--access_token [access_token]", "AccessToken @ Qiita") do |access_token|
|
49
|
+
@access_token = access_token.strip
|
50
|
+
end
|
51
|
+
|
52
|
+
opts.on("-e", "--email [email]", "Email (Gmail)") do |email|
|
53
|
+
@email = email.strip
|
54
|
+
end
|
55
|
+
|
56
|
+
opts.on("-p", "--password [password]", "Password (Gmail)") do |password|
|
57
|
+
@password = password.strip
|
58
|
+
end
|
59
|
+
|
60
|
+
opts.on("-s", "--send_mail_flag [send_mail_flag]", "Send email flag <boolean>") do |v|
|
61
|
+
@send_mail = ( v == "true" )
|
62
|
+
end
|
63
|
+
|
64
|
+
opts.on("-t", "--tag [tag]", "Tag") do |tag|
|
65
|
+
@tag = tag.strip
|
66
|
+
end
|
67
|
+
|
68
|
+
opts.on("-u", "--username [username]", "UserName") do |username|
|
69
|
+
@username = username.strip
|
70
|
+
end
|
71
|
+
|
72
|
+
opts.on_tail("-?", "--help", "Show this message") do
|
73
|
+
puts opts
|
74
|
+
exit
|
75
|
+
end
|
76
|
+
|
77
|
+
opts.on_tail("-v", "--version", "Show version") do
|
78
|
+
puts "version #{QiitaPicks::VERSION} on Ruby #{RUBY_VERSION}"
|
79
|
+
exit
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
opts.parse!
|
84
|
+
|
85
|
+
command = ARGV.shift
|
86
|
+
|
87
|
+
if command.to_s.strip.empty?
|
88
|
+
puts opts
|
89
|
+
exit 1
|
90
|
+
end
|
91
|
+
|
92
|
+
unless QiitaPicks.wkhtmltopdf_installed?
|
93
|
+
puts <<-EOF
|
94
|
+
[ERROR] wkhtmltopdf not found"
|
95
|
+
See at: http://wkhtmltopdf.org/downloads.html
|
96
|
+
EOF
|
97
|
+
exit 1
|
98
|
+
end
|
99
|
+
|
100
|
+
begin
|
101
|
+
generator = QiitaPicks::Generator.new(email: @email, password: @password, access_token: @access_token)
|
102
|
+
@send_mail ||= false
|
103
|
+
|
104
|
+
case command
|
105
|
+
when 'list_user_items'
|
106
|
+
generator.generate_from_list_user_items(@username, deliver: [@email, @password].none?(&:nil?))
|
107
|
+
exit
|
108
|
+
when 'list_user_stocks'
|
109
|
+
generator.generate_from_list_user_stocks(@username, deliver: [@email, @password].none?(&:nil?))
|
110
|
+
exit
|
111
|
+
when 'list_tag_items'
|
112
|
+
generator.generate_from_list_tag_items(@tag, deliver: [@email, @password].none?(&:nil?))
|
113
|
+
exit
|
114
|
+
when 'list_hatena_popular'
|
115
|
+
generator.generate_from_list_hatebu_popular(tag: @tag, deliver: [@email, @password].none?(&:nil?))
|
116
|
+
exit
|
117
|
+
when 'list_hatena_recent'
|
118
|
+
generator.generate_from_list_hatebu_recent(tag: @tag, deliver: [@email, @password].none?(&:nil?))
|
119
|
+
exit
|
120
|
+
when 'email_weekly_popular'
|
121
|
+
generator.generate_from_email_weekly_popular(deliver: @send_mail)
|
122
|
+
exit
|
123
|
+
when 'email_weekly_stocked'
|
124
|
+
generator.generate_from_email_weekly_stocked(deliver: @send_mail)
|
125
|
+
exit
|
126
|
+
when /^https*\:\/\/.+/
|
127
|
+
urls = command.split(",").map(&:strip)
|
128
|
+
generator.generate_from_url(urls, deliver: [@email, @password].none?(&:nil?))
|
129
|
+
exit
|
130
|
+
else
|
131
|
+
puts opts
|
132
|
+
exit 1
|
133
|
+
end
|
134
|
+
rescue => e
|
135
|
+
puts "[ERROR] #{e.class} - #{e.message}"
|
136
|
+
exit 1
|
137
|
+
end
|
data/lib/qiita_picks.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require "qiita_picks/version"
|
2
|
+
require "qiita_picks/generator"
|
3
|
+
require "qiita_picks/mailer"
|
4
|
+
require "qiita_picks/picker"
|
5
|
+
|
6
|
+
module QiitaPicks
|
7
|
+
FILE_BASENAME_DEFAULT = "%Y%m%d%H%M_qiita_picks"
|
8
|
+
FILE_BASENAME_EMAIL_WEEKLY_POPULAR = "%Y%m%d_email_weekly_popular"
|
9
|
+
FILE_BASENAME_EMAIL_WEEKLY_STOCKED = "%Y%m%d_email_weekly_stocked"
|
10
|
+
FILE_BASENAME_LIST_TAG_ITEMS = "%Y%m%d%H%M_list_tag_items"
|
11
|
+
FILE_BASENAME_LIST_USER_ITEMS = "%Y%m%d%H%M_list_user_items"
|
12
|
+
FILE_BASENAME_LIST_USER_STOCKS = "%Y%m%d%H%M_list_user_stocks"
|
13
|
+
FILE_BASENAME_LIST_HATEBU_POPULAR = "%Y%m%d%H%M_list_hatebu_popular"
|
14
|
+
FILE_BASENAME_LIST_HATEBU_RECENT = "%Y%m%d%H%M_list_hatebu_recent"
|
15
|
+
|
16
|
+
EMAIL_SUBJECT_DEFAULT = "[QiitaPicks] Qiitaの記事まとめ - %Y/%m/%d"
|
17
|
+
EMAIL_SUBJECT_EMAIL_WEEKLY_POPULAR = "[QiitaPicks] Qiitaでストックが多かった投稿 - %Y/%m/%d 配信"
|
18
|
+
EMAIL_SUBJECT_EMAIL_WEEKLY_STOCKED = "[QiitaPicks] <SUBJECT> - %Y/%m/%d 配信"
|
19
|
+
EMAIL_SUBJECT_LIST_TAG_ITEMS = "[QiitaPicks] <TAG>に関するQiitaの記事まとめ - %Y/%m/%d"
|
20
|
+
EMAIL_SUBJECT_LIST_USER_ITEMS = "[QiitaPicks] <USERNAME>に関するQiitaの記事まとめ - %Y/%m/%d"
|
21
|
+
EMAIL_SUBJECT_LIST_USER_STOCKS = "[QiitaPicks] <USERNAME>がストックしたQiitaの記事まとめ - %Y/%m/%d"
|
22
|
+
EMAIL_SUBJECT_LIST_HATEBU_POPULAR = "[QiitaPicks] はてブで人気のQiitaの記事まとめ - %Y/%m/%d"
|
23
|
+
EMAIL_SUBJECT_LIST_HATEBU_RECENT = "[QiitaPicks] はてブで新着のQiitaの記事まとめ - %Y/%m/%d"
|
24
|
+
|
25
|
+
REPOSITORY_URL = "https://github.com/henteco-labs/qiita_picks"
|
26
|
+
REPOSITORY_URL_SHORTEN = "http://goo.gl/QUkuEY"
|
27
|
+
FEEDBACK_URL_SHORTEN = "http://goo.gl/forms/PGFggwhCu1"
|
28
|
+
|
29
|
+
EMAIL_CONTENT_DEFAULT = <<-EOF
|
30
|
+
Generated by QiitaPicks version #{QiitaPicks::VERSION} on Ruby #{RUBY_VERSION}
|
31
|
+
GitHub: #{QiitaPicks::REPOSITORY_URL_SHORTEN}
|
32
|
+
|
33
|
+
Please send me your feedback:)
|
34
|
+
#{QiitaPicks::FEEDBACK_URL_SHORTEN}
|
35
|
+
EOF
|
36
|
+
HTML_FOOTER_CREDIT = <<-EOF
|
37
|
+
<footer>
|
38
|
+
<p>Generated by QiitaPicks version #{QiitaPicks::VERSION} on Ruby #{RUBY_VERSION}</p>
|
39
|
+
<p>GitHub: <a href="#{QiitaPicks::REPOSITORY_URL_SHORTEN}">#{QiitaPicks::REPOSITORY_URL}</a></p>
|
40
|
+
<p>Please send me your feedback:)</p>
|
41
|
+
<p><a href="#{QiitaPicks::FEEDBACK_URL_SHORTEN}">#{QiitaPicks::FEEDBACK_URL_SHORTEN}</a></p>
|
42
|
+
</footer>
|
43
|
+
EOF
|
44
|
+
|
45
|
+
def self.wkhtmltopdf_installed?
|
46
|
+
/^\// =~ `which wkhtmltopdf`.to_s
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# external dependencies
|
2
|
+
require 'qiita'
|
3
|
+
|
4
|
+
# stdlib dependencies
|
5
|
+
require 'tmpdir'
|
6
|
+
|
7
|
+
module QiitaPicks
|
8
|
+
class Generator
|
9
|
+
def initialize(params={})
|
10
|
+
params[:email] ||= ''
|
11
|
+
params[:password] ||= ''
|
12
|
+
params[:access_token] ||= ''
|
13
|
+
@email = params[:email]
|
14
|
+
@mailer = QiitaPicks::Mailer.new(params[:email], params[:password])
|
15
|
+
@qiita = Qiita::Client.new(access_token: params[:access_token])
|
16
|
+
end
|
17
|
+
|
18
|
+
def generate_from_url(url=[], opts={})
|
19
|
+
opts[:deliver] ||= false
|
20
|
+
opts[:deliver] ? generate_and_deliver(url: url) : generate(url: url)
|
21
|
+
end
|
22
|
+
|
23
|
+
def generate_from_email_weekly_popular(opts={})
|
24
|
+
opts[:limit] ||= 1
|
25
|
+
opts[:deliver] ||= false
|
26
|
+
@mailer.weekly_popular(opts[:limit]).each do |mail|
|
27
|
+
@basename = mail[:date].strftime QiitaPicks::FILE_BASENAME_EMAIL_WEEKLY_POPULAR
|
28
|
+
@subject = mail[:date].strftime QiitaPicks::EMAIL_SUBJECT_EMAIL_WEEKLY_POPULAR
|
29
|
+
opts[:deliver] ? generate_and_deliver(url: mail[:url]) : generate(url: mail[:url])
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def generate_from_email_weekly_stocked(opts={})
|
34
|
+
opts[:limit] ||= 1
|
35
|
+
opts[:deliver] ||= false
|
36
|
+
@mailer.weekly_stocked(opts[:limit]).each do |mail|
|
37
|
+
@basename = mail[:date].strftime QiitaPicks::FILE_BASENAME_EMAIL_WEEKLY_STOCKED
|
38
|
+
@subject = mail[:date].strftime(QiitaPicks::EMAIL_SUBJECT_EMAIL_WEEKLY_STOCKED).gsub(/<SUBJECT>/, mail[:subject])
|
39
|
+
opts[:deliver] ? generate_and_deliver(url: mail[:url]) : generate(url: mail[:url])
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def generate_from_list_tag_items(tag, opts={})
|
44
|
+
opts[:page] ||= 1
|
45
|
+
opts[:per_page] ||= 20
|
46
|
+
opts[:deliver] ||= false
|
47
|
+
@basename = Time.now.strftime QiitaPicks::FILE_BASENAME_LIST_TAG_ITEMS
|
48
|
+
@subject = Time.now.strftime(QiitaPicks::EMAIL_SUBJECT_LIST_TAG_ITEMS).gsub(/<TAG>/, tag.capitalize)
|
49
|
+
items = @qiita.list_tag_items(tag, page: opts[:page], per_page: opts[:per_page]).body
|
50
|
+
opts[:deliver] ? generate_and_deliver(items: items) : generate(items: items)
|
51
|
+
end
|
52
|
+
|
53
|
+
def generate_from_list_user_items(username, opts={})
|
54
|
+
opts[:page] ||= 1
|
55
|
+
opts[:per_page] ||= 20
|
56
|
+
opts[:deliver] ||= false
|
57
|
+
@basename = Time.now.strftime QiitaPicks::FILE_BASENAME_LIST_USER_ITEMS
|
58
|
+
@subject = Time.now.strftime(QiitaPicks::EMAIL_SUBJECT_LIST_USER_ITEMS).gsub(/<USERNAME>/, username)
|
59
|
+
items = @qiita.list_user_items(username, page: opts[:page], per_page: opts[:per_page]).body
|
60
|
+
opts[:deliver] ? generate_and_deliver(items: items) : generate(items: items)
|
61
|
+
end
|
62
|
+
|
63
|
+
def generate_from_list_user_stocks(username, opts={})
|
64
|
+
opts[:page] ||= 1
|
65
|
+
opts[:per_page] ||= 20
|
66
|
+
opts[:deliver] ||= false
|
67
|
+
@basename = Time.now.strftime QiitaPicks::FILE_BASENAME_LIST_USER_STOCKS
|
68
|
+
@subject = Time.now.strftime(QiitaPicks::EMAIL_SUBJECT_LIST_USER_STOCKS).gsub(/<USERNAME>/, username)
|
69
|
+
items = @qiita.list_user_stocks(username, page: opts[:page], per_page: opts[:per_page]).body
|
70
|
+
opts[:deliver] ? generate_and_deliver(items: items) : generate(items: items)
|
71
|
+
end
|
72
|
+
|
73
|
+
def generate_from_list_hatebu_popular(opts={})
|
74
|
+
opts[:deliver] ||= false
|
75
|
+
@basename = Time.now.strftime QiitaPicks::FILE_BASENAME_LIST_HATEBU_POPULAR
|
76
|
+
@subject = Time.now.strftime QiitaPicks::EMAIL_SUBJECT_LIST_HATEBU_POPULAR
|
77
|
+
url = QiitaPicks::Picker.hatebu_bookmark_url opts[:tag], :popular
|
78
|
+
opts[:deliver] ? generate_and_deliver(url: url) : generate(url: url)
|
79
|
+
end
|
80
|
+
|
81
|
+
def generate_from_list_hatebu_recent(opts={})
|
82
|
+
opts[:deliver] ||= false
|
83
|
+
@basename = Time.now.strftime QiitaPicks::FILE_BASENAME_LIST_HATEBU_RECENT
|
84
|
+
@subject = Time.now.strftime QiitaPicks::EMAIL_SUBJECT_LIST_HATEBU_RECENT
|
85
|
+
url = QiitaPicks::Picker.hatebu_bookmark_url opts[:tag], :recent
|
86
|
+
opts[:deliver] ? generate_and_deliver(url: url) : generate(url: url)
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
def generate(params={})
|
92
|
+
params[:url] ||= []
|
93
|
+
params[:items] ||= []
|
94
|
+
params[:dest] ||= '.'
|
95
|
+
@basename ||= Time.now.strftime QiitaPicks::FILE_BASENAME_DEFAULT
|
96
|
+
@subject ||= Time.now.strftime QiitaPicks::EMAIL_SUBJECT_DEFAULT
|
97
|
+
@picker = QiitaPicks::Picker.new
|
98
|
+
@picker.build_html_from_url(params[:url], @subject)
|
99
|
+
@picker.build_html_from_items(params[:items], @subject)
|
100
|
+
@picker.save_as_pdf(dest: params[:dest], basename: @basename)
|
101
|
+
@picker.save_as_html(dest: params[:dest], basename: @basename)
|
102
|
+
end
|
103
|
+
|
104
|
+
def generate_and_deliver(params={})
|
105
|
+
Dir.mktmpdir do |dir|
|
106
|
+
generate params.merge({dest: dir})
|
107
|
+
@mailer.deliver(
|
108
|
+
to: @email,
|
109
|
+
subject: @subject,
|
110
|
+
body: QiitaPicks::EMAIL_CONTENT_DEFAULT,
|
111
|
+
add_files: [@picker.path_to_pdf, @picker.path_to_html]
|
112
|
+
)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# external dependencies
|
2
|
+
require 'gmail'
|
3
|
+
require 'nokogiri'
|
4
|
+
|
5
|
+
# stdlib dependencies
|
6
|
+
require 'nkf'
|
7
|
+
|
8
|
+
module QiitaPicks
|
9
|
+
class Mailer
|
10
|
+
def initialize(email, password)
|
11
|
+
@gmail = Gmail.new(email, password)
|
12
|
+
end
|
13
|
+
|
14
|
+
def deliver(params={})
|
15
|
+
return if params[:add_files].compact.empty?
|
16
|
+
@gmail.deliver {
|
17
|
+
to params[:to]
|
18
|
+
subject params[:subject]
|
19
|
+
body params[:body]
|
20
|
+
params[:add_files].each {|f| add_file(f)}
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
def weekly_popular(limit=5)
|
25
|
+
@gmail.inbox.emails(:unread, from: "info@qiita.com").last(limit).inject([]) do |data, mail|
|
26
|
+
url = mail.text_part.decoded.scan(/^\d+位.+\((http.+?)\)/).flatten
|
27
|
+
data << Hash[:subject, mail.subject, :date, mail.date, :url, url]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def weekly_stocked(limit=5)
|
32
|
+
@gmail.inbox.emails(:unread, from: "noreply@qiita.com").last(limit).inject([]) do |data, mail|
|
33
|
+
next unless /^.+?さんが今週Qiitaでストックした記事$/ =~ mail.subject
|
34
|
+
doc = Nokogiri::HTML NKF.nkf('-w', NKF.nkf('-w', mail.body.decoded))
|
35
|
+
url = doc.css('.stockedArticlesTable a').inject([]){|list, elem|
|
36
|
+
list << elem.attr("href").match(/(^http.+?)\?/)[1]
|
37
|
+
}.uniq
|
38
|
+
data << Hash[:subject, mail.subject, :date, mail.date, :url, url]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
# external dependencies
|
2
|
+
require 'nokogiri'
|
3
|
+
require 'pdfkit'
|
4
|
+
require 'baby_erubis'
|
5
|
+
require 'active_support'
|
6
|
+
require 'active_support/core_ext'
|
7
|
+
|
8
|
+
# stdlib dependencies
|
9
|
+
require 'date'
|
10
|
+
require 'nkf'
|
11
|
+
require 'uri'
|
12
|
+
require 'open-uri'
|
13
|
+
require 'net/http'
|
14
|
+
|
15
|
+
module QiitaPicks
|
16
|
+
class Picker
|
17
|
+
ERB_TEMPLATE_ARTICLE = File.expand_path '../templates/article.erb', __FILE__
|
18
|
+
ERB_TEMPLATE_LAYOUT = File.expand_path '../templates/layout.erb', __FILE__
|
19
|
+
PDFKIT_OPTIONS = {
|
20
|
+
encoding: 'UTF-8',
|
21
|
+
page_size: 'A4',
|
22
|
+
quiet: false,
|
23
|
+
no_background: false
|
24
|
+
}.freeze
|
25
|
+
|
26
|
+
attr_reader :path_to_pdf, :path_to_html
|
27
|
+
|
28
|
+
def self.hatebu_bookmark_url(tag=nil, sort=:popular)
|
29
|
+
uri = URI('http://b.hatena.ne.jp/search/text')
|
30
|
+
uri.query = {
|
31
|
+
q: ['http://qiita.com/', tag].compact.join(' '),
|
32
|
+
date_begin: (Time.now - 60*60*24*30).strftime('%Y-%m-%d'),
|
33
|
+
date_end: Time.now.strftime('%Y-%m-%d'),
|
34
|
+
users: 3,
|
35
|
+
sort: sort,
|
36
|
+
safe: 'off',
|
37
|
+
mode: 'rss'
|
38
|
+
}.to_param
|
39
|
+
doc = Nokogiri::XML open(uri.to_s)
|
40
|
+
doc.xpath("//rdf:Seq/rdf:li").map{|x| x.attr("rdf:resource")}
|
41
|
+
end
|
42
|
+
|
43
|
+
def build_html_from_url(url=[], title=nil)
|
44
|
+
return if url.empty?
|
45
|
+
@title = title || Time.now.strftime(QiitaPicks::EMAIL_SUBJECT_DEFAULT)
|
46
|
+
@contents ||= build_articles_from_url url
|
47
|
+
@html ||= html
|
48
|
+
end
|
49
|
+
|
50
|
+
def build_html_from_items(items=[], title=nil)
|
51
|
+
return if items.empty?
|
52
|
+
@title = title || Time.now.strftime(QiitaPicks::EMAIL_SUBJECT_DEFAULT)
|
53
|
+
@contents ||= build_articles_from_items items
|
54
|
+
@html ||= html
|
55
|
+
end
|
56
|
+
|
57
|
+
def save_as_pdf(opts={})
|
58
|
+
return if @html.nil?
|
59
|
+
opts[:dest] ||= '.'
|
60
|
+
opts[:basename] ||= Time.now.strftime QiitaPicks::FILE_BASENAME_DEFAULT
|
61
|
+
opts[:pdf_options] ||= {}
|
62
|
+
@path_to_pdf = File.join(opts[:dest], "#{opts[:basename]}.pdf")
|
63
|
+
PDFKit.new(@html, PDFKIT_OPTIONS.merge(opts[:pdf_options])).to_file @path_to_pdf
|
64
|
+
puts "[GENERATE] #{@path_to_pdf}"
|
65
|
+
end
|
66
|
+
|
67
|
+
def save_as_html(opts={})
|
68
|
+
return if @html.nil?
|
69
|
+
opts[:dest] ||= '.'
|
70
|
+
opts[:basename] ||= Time.now.strftime QiitaPicks::FILE_BASENAME_DEFAULT
|
71
|
+
@path_to_html = File.join(opts[:dest], "#{opts[:basename]}.html")
|
72
|
+
open(@path_to_html, "w+") {|f| f.write @html}
|
73
|
+
puts "[GENERATE] #{@path_to_html}"
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def html
|
79
|
+
return if @contents.empty?
|
80
|
+
template = BabyErubis::Html.new.from_file(ERB_TEMPLATE_LAYOUT, 'utf-8')
|
81
|
+
template.render(title: @title, contents: @contents, credit: HTML_FOOTER_CREDIT)
|
82
|
+
end
|
83
|
+
|
84
|
+
def build_articles_from_url(urls=[])
|
85
|
+
urls.select {|url| /qiita/ =~ URI.parse(url).host}.uniq.inject('') {|articles, url|
|
86
|
+
if res = fetch(url)
|
87
|
+
uri = res.uri
|
88
|
+
doc = Nokogiri::HTML NKF.nkf('-w', res.body)
|
89
|
+
template = BabyErubis::Html.new.from_file(ERB_TEMPLATE_ARTICLE, 'utf-8')
|
90
|
+
context = {
|
91
|
+
url: "#{uri.scheme}://#{uri.host}#{uri.path}",
|
92
|
+
title: doc.css('title').text,
|
93
|
+
content: doc.css('.markdownContent').inner_html,
|
94
|
+
user_name: doc.css('.itemsShowAuthorInfo_userName').text,
|
95
|
+
created_at: doc.css('ul.itemsShowHeaderTitle_status time').first.text
|
96
|
+
}
|
97
|
+
articles << template.render(context)
|
98
|
+
end
|
99
|
+
articles
|
100
|
+
}
|
101
|
+
end
|
102
|
+
|
103
|
+
def build_articles_from_items(items=[])
|
104
|
+
items.inject('') {|articles, item|
|
105
|
+
template = BabyErubis::Html.new.from_file(ERB_TEMPLATE_ARTICLE, 'utf-8')
|
106
|
+
context = {
|
107
|
+
url: item['url'],
|
108
|
+
title: item['title'],
|
109
|
+
content: item['rendered_body'],
|
110
|
+
user_name: item['user']['id'],
|
111
|
+
created_at: DateTime.parse(item['created_at']).strftime('%Y/%m/%d')
|
112
|
+
}
|
113
|
+
articles << template.render(context)
|
114
|
+
}
|
115
|
+
end
|
116
|
+
|
117
|
+
def fetch(url, limit=10)
|
118
|
+
res = Net::HTTP.get_response URI.parse(url)
|
119
|
+
puts "[#{res.code} #{res.message}] #{url}"
|
120
|
+
case res
|
121
|
+
when Net::HTTPSuccess
|
122
|
+
return res
|
123
|
+
when Net::HTTPRedirection
|
124
|
+
return fetch(res['location'], limit - 1)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,5 @@
|
|
1
|
+
<header>
|
2
|
+
<h1><%= @title %></h1>
|
3
|
+
<p>Posted by: <%= @user_name %> | Created at: <%= @created_at %> | <a href="<%= @url %>">Permalink</a></p>
|
4
|
+
</header>
|
5
|
+
<section class="markdownContent markdownContent-headingEnabled"><%== @content %></section>
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="ja">
|
3
|
+
<head>
|
4
|
+
<meta charset="utf-8" />
|
5
|
+
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no" />
|
6
|
+
<meta name="generator" content="QiitaPicks" />
|
7
|
+
<title><%= @title %></title>
|
8
|
+
<style type="text/css" media="all">body{font-size:11pt!important;background-color:#fff;color:#000;font-family:"Helvetica Neue",Helvetica,"游ゴシック",YuGothic,"ヒラギノ角ゴ ProN W3","Hiragino Kaku Gothic ProN",sans-serif;-webkit-font-smoothing:antialiased!important}a{color:#337ab7;text-decoration:underline}header{padding:.5em 0;border-bottom:.2em solid #79b74a;page-break-before:always}header:first-child{page-break-before:avoid}header h1{margin:0;color:#79b74a;font-size:19pt!important}header p{margin:.5em 0 0 0;color:#999;font-size:9pt!important;text-align:right}section{padding-top:2em}.hidden{display:none}.markdownContent>:first-child{margin-top:0!important}.markdownContent-headingEnabled h1,.markdownContent-headingEnabled h2,.markdownContent-headingEnabled h3,.markdownContent-headingEnabled h4,.markdownContent-headingEnabled h5,.markdownContent-headingEnabled h6{position:relative}.markdownContent blockquote{font-size:11pt;margin-top:1em;margin-bottom:1em;margin-left:.8em;padding:.8em;color:#555;border-left:solid 4px #ddd}.markdownContent blockquote>:first-child{margin-top:0}.markdownContent blockquote h1,.markdownContent blockquote h2,.markdownContent blockquote h3,.markdownContent blockquote h4,.markdownContent blockquote h5,.markdownContent blockquote h6{color:#555}.markdownContent a{word-wrap:break-word;word-break:break-all}.markdownContent hr{height:2px;margin:1em 0;border:0;background-color:#ccc}.markdownContent em{font-family:"Helvetica Neue",Helvetica,"ヒラギノ角ゴ ProN W3","Hiragino Kaku Gothic ProN","游ゴシック",YuGothic,sans-serif;font-style:italic}.markdownContent b,.markdownContent ins,.markdownContent strong{font-weight:900}.markdownContent del{text-decoration:line-through}.markdownContent p{font-size:11pt;line-height:1.7;margin:1em 0;word-wrap:break-word}.markdownContent img{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;max-width:100%;margin:0;vertical-align:middle}.markdownContent dl{margin:1em 0}.markdownContent dl dd{margin-left:2em}.markdownContent table{margin:1em 0;border-top:solid 1px #d9d9d9;border-left:solid 1px #d9d9d9}.markdownContent td,.markdownContent th{padding:8px 10px;word-wrap:break-word;border-right:solid 1px #ccc;border-bottom:solid 1px #ccc}.markdownContent th,.markdownContent thead td{font-weight:900;background-color:#fff}.markdownContent tbody tr:nth-child(odd){background-color:rgba(0,0,0,.03)}.markdownContent li p:first-child{margin:0}.markdownContent ol,.markdownContent ul{line-height:1.7;margin-top:1em;margin-bottom:1em;padding-left:1.5em}.markdownContent ol>li{list-style-type:decimal}.markdownContent ul>li{list-style-type:disc}.markdownContent li>ol,.markdownContent li>ul{margin-top:0;margin-bottom:0}.markdownContent h1,.markdownContent h2,.markdownContent h3,.markdownContent h4,.markdownContent h5,.markdownContent h6{font-size:11pt;font-family:sans-serif;line-height:1.4;margin:1.35em 0 .7em}.markdownContent h1{line-height:1.2;padding-bottom:.3em;border-bottom:2px solid #dedede}.markdownContent h1 .emoji{width:1.05em;height:1.05em;vertical-align:top}.markdownContent h2{line-height:1.2;padding-bottom:.3em;border-bottom:1px dotted #ccc}.markdownContent h2 .emoji{width:1.08em;height:1.08em;vertical-align:top}.markdownContent h3{line-height:1.4;margin-top:1.4em}.markdownContent h4{margin-top:1.3em}.markdownContent h5{margin-top:1.5em}.markdownContent h6{margin-top:1.5em;color:#777}.markdownContent .code-frame{line-height:0;margin:1em 0;border-radius:3px;background-color:#f7f7f7}.markdownContent .code-frame pre{font-family:Consolas,"Liberation Mono",Menlo,Courier,monospace;line-height:18px;margin:0;padding:8px 15px;white-space:pre-wrap;border:0;border-radius:3px;background-color:#f7f7f7}.markdownContent .code-lang{font-size:9pt;font-weight:400;line-height:1;display:inline-block;margin:0;padding:3px 6px;color:#555;background-color:rgba(0,0,0,.07)}.markdownContent .emoji{width:20px;height:20px}.markdownContent .fa.fa-link,.markdownContent .icon-link{font-size:16px;font-weight:400;position:absolute;top:50%;left:-28px;display:none;margin-top:-18px;padding:10px 5px 10px 10px;color:#555}.markdownContent .footnotes{font-size:.9em;margin-top:2em}.markdownContent .footnotes hr{display:none}.markdownContent .fragment{position:relative;top:-10px;display:block}.markdownContent .highlight{background:#fff}.markdownContent .highlight .c{font-style:italic;color:#998}.markdownContent .highlight .err{color:#a61717;background-color:#e3d2d2}.markdownContent .highlight .k,.markdownContent .highlight .o{font-weight:700}.markdownContent .highlight .cm{font-style:italic;color:#998}.markdownContent .highlight .cp{font-weight:700;color:#999}.markdownContent .highlight .c1{font-style:italic;color:#998}.markdownContent .highlight .cs{font-weight:700;font-style:italic;color:#999}.markdownContent .highlight .gd{color:#000;background-color:#fdd}.markdownContent .highlight .gd .x{color:#000;background-color:#faa}.markdownContent .highlight .ge{font-style:italic}.markdownContent .highlight .gr{color:#a00}.markdownContent .highlight .gh{color:#999}.markdownContent .highlight .gi{color:#000;background-color:#dfd}.markdownContent .highlight .gi .x{color:#000;background-color:#afa}.markdownContent .highlight .go{color:#888}.markdownContent .highlight .gp{color:#555}.markdownContent .highlight .gs{font-weight:700}.markdownContent .highlight .gu{font-weight:700;color:purple}.markdownContent .highlight .gt{color:#a00}.markdownContent .highlight .kc,.markdownContent .highlight .kd,.markdownContent .highlight .kn,.markdownContent .highlight .kp,.markdownContent .highlight .kr{font-weight:700}.markdownContent .highlight .kt{font-weight:700;color:#458}.markdownContent .highlight .m{color:#099}.markdownContent .highlight .s{color:#d14}.markdownContent .highlight .n{color:#333}.markdownContent .highlight .na{color:teal}.markdownContent .highlight .nb{color:#0086b3}.markdownContent .highlight .nc{font-weight:700;color:#458}.markdownContent .highlight .no{color:teal}.markdownContent .highlight .ni{color:purple}.markdownContent .highlight .ne,.markdownContent .highlight .nf{font-weight:700;color:#900}.markdownContent .highlight .nn{color:#555}.markdownContent .highlight .nt{color:navy}.markdownContent .highlight .nv{color:teal}.markdownContent .highlight .ow{font-weight:700}.markdownContent .highlight .w{color:#bbb}.markdownContent .highlight .mf,.markdownContent .highlight .mh,.markdownContent .highlight .mi,.markdownContent .highlight .mo{color:#099}.markdownContent .highlight .s2,.markdownContent .highlight .sb,.markdownContent .highlight .sc,.markdownContent .highlight .sd,.markdownContent .highlight .se,.markdownContent .highlight .sh,.markdownContent .highlight .si,.markdownContent .highlight .sx{color:#d14}.markdownContent .highlight .sr{color:#009926}.markdownContent .highlight .s1{color:#d14}.markdownContent .highlight .ss{color:#990073}.markdownContent .highlight .bp{color:#999}.markdownContent .highlight .vc,.markdownContent .highlight .vg,.markdownContent .highlight .vi{color:teal}.markdownContent .highlight .il{color:#099}.markdownContent .highlight .gc{color:#999;background-color:#eaf2f5}.markdownContent .MathJax img,.markdownContent .MathJax_Display img{border:0!important}.markdownContent .parenthesis li{list-style-type:none}.markdownContent .parenthesis li span{margin-right:.5em;margin-left:-1.5em}.markdownContent .task-list-item{list-style-type:none}.markdownContent .task-list-item-checkbox{margin:.4em .5em 0 -1.3em}.markdownContent .task-list-item-checkbox[disabled]{cursor:default}footer{margin:0;padding:1em 0 0 0;page-break-before:always}footer p {font-family:Consolas,"Liberation Mono",Menlo,Courier,monospace}</style>
|
9
|
+
</head>
|
10
|
+
<body><%== @contents %><%== @credit %></body>
|
11
|
+
</html>
|
data/qiita_picks.gemspec
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'qiita_picks/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "qiita_picks"
|
8
|
+
spec.version = QiitaPicks::VERSION
|
9
|
+
spec.authors = ["Yoshiyuki Hirano"]
|
10
|
+
spec.email = ["info@henteco-labs.com"]
|
11
|
+
spec.description = %q{Generate PDF/HTML from articles posted on qiita.com. Uses wkhtmltopdf on the back-end which renders HTML using Webkit.}
|
12
|
+
spec.summary = %q{Generate PDF/HTML from articles posted on qiita.com.}
|
13
|
+
spec.homepage = "https://github.com/henteco-labs/qiita_picks"
|
14
|
+
spec.license = "MIT"
|
15
|
+
spec.post_install_message = <<-EOF
|
16
|
+
|
17
|
+
qiita_picks installed! To get started fast:
|
18
|
+
|
19
|
+
$ qiita_picks <URLs>
|
20
|
+
|
21
|
+
Or see the README.md at:
|
22
|
+
|
23
|
+
https://github.com/henteco-labs/qiita_picks
|
24
|
+
|
25
|
+
EOF
|
26
|
+
|
27
|
+
spec.add_dependency "nokogiri", "~> 1.6"
|
28
|
+
spec.add_dependency "pdfkit", "~> 0.6"
|
29
|
+
spec.add_dependency "ruby-gmail", "~> 0.3"
|
30
|
+
spec.add_dependency "qiita", "~> 1.1"
|
31
|
+
spec.add_dependency "activesupport", "~> 4.1"
|
32
|
+
spec.add_dependency "baby_erubis", "~> 2.0"
|
33
|
+
|
34
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
35
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
36
|
+
spec.add_development_dependency "rspec", "~> 0"
|
37
|
+
spec.add_development_dependency "pry", "~> 0"
|
38
|
+
|
39
|
+
spec.files = `git ls-files -z`.split("\x0")
|
40
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
41
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
42
|
+
spec.require_paths = ["lib"]
|
43
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,215 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: qiita_picks
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Yoshiyuki Hirano
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-03-06 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: nokogiri
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.6'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.6'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: pdfkit
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.6'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.6'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: ruby-gmail
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.3'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.3'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: qiita
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.1'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.1'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: activesupport
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '4.1'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '4.1'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: baby_erubis
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '2.0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '2.0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: bundler
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '1.7'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '1.7'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rake
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '10.0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '10.0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: rspec
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: pry
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - "~>"
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - "~>"
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
description: Generate PDF/HTML from articles posted on qiita.com. Uses wkhtmltopdf
|
154
|
+
on the back-end which renders HTML using Webkit.
|
155
|
+
email:
|
156
|
+
- info@henteco-labs.com
|
157
|
+
executables:
|
158
|
+
- qiita_picks
|
159
|
+
extensions: []
|
160
|
+
extra_rdoc_files: []
|
161
|
+
files:
|
162
|
+
- ".gitignore"
|
163
|
+
- ".rspec"
|
164
|
+
- ".travis.yml"
|
165
|
+
- Gemfile
|
166
|
+
- LICENSE.txt
|
167
|
+
- README.md
|
168
|
+
- Rakefile
|
169
|
+
- bin/qiita_picks
|
170
|
+
- lib/qiita_picks.rb
|
171
|
+
- lib/qiita_picks/generator.rb
|
172
|
+
- lib/qiita_picks/mailer.rb
|
173
|
+
- lib/qiita_picks/picker.rb
|
174
|
+
- lib/qiita_picks/templates/article.erb
|
175
|
+
- lib/qiita_picks/templates/layout.erb
|
176
|
+
- lib/qiita_picks/version.rb
|
177
|
+
- qiita_picks.gemspec
|
178
|
+
- spec/qiita_picks_spec.rb
|
179
|
+
- spec/spec_helper.rb
|
180
|
+
homepage: https://github.com/henteco-labs/qiita_picks
|
181
|
+
licenses:
|
182
|
+
- MIT
|
183
|
+
metadata: {}
|
184
|
+
post_install_message: |2+
|
185
|
+
|
186
|
+
qiita_picks installed! To get started fast:
|
187
|
+
|
188
|
+
$ qiita_picks <URLs>
|
189
|
+
|
190
|
+
Or see the README.md at:
|
191
|
+
|
192
|
+
https://github.com/henteco-labs/qiita_picks
|
193
|
+
|
194
|
+
rdoc_options: []
|
195
|
+
require_paths:
|
196
|
+
- lib
|
197
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
198
|
+
requirements:
|
199
|
+
- - ">="
|
200
|
+
- !ruby/object:Gem::Version
|
201
|
+
version: '0'
|
202
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
203
|
+
requirements:
|
204
|
+
- - ">="
|
205
|
+
- !ruby/object:Gem::Version
|
206
|
+
version: '0'
|
207
|
+
requirements: []
|
208
|
+
rubyforge_project:
|
209
|
+
rubygems_version: 2.2.2
|
210
|
+
signing_key:
|
211
|
+
specification_version: 4
|
212
|
+
summary: Generate PDF/HTML from articles posted on qiita.com.
|
213
|
+
test_files:
|
214
|
+
- spec/qiita_picks_spec.rb
|
215
|
+
- spec/spec_helper.rb
|