qiita_picks 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+ .DS_Store
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.5
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in qiita_picks.gemspec
4
+ gemspec
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
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
7
+
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
@@ -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:&nbsp;<%= @user_name %>&nbsp;|&nbsp;Created at: <%= @created_at %>&nbsp;|&nbsp;<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>
@@ -0,0 +1,3 @@
1
+ module QiitaPicks
2
+ VERSION = "0.1.2"
3
+ end
@@ -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
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe QiitaPicks do
4
+ it 'has a version number' do
5
+ expect(QiitaPicks::VERSION).not_to be nil
6
+ end
7
+ end
@@ -0,0 +1,2 @@
1
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
+ require 'qiita_picks'
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