qiita_matome 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.coveralls.yml +1 -0
- data/.gitignore +22 -0
- data/.rspec +2 -0
- data/.rubocop.yml +0 -0
- data/.travis.yml +8 -0
- data/CONTRIBUTING.md +44 -0
- data/Gemfile +13 -0
- data/LICENSE.txt +22 -0
- data/README.md +210 -0
- data/Rakefile +8 -0
- data/bin/qiitam +34 -0
- data/lib/display.rb +10 -0
- data/lib/display/display_consts.rb +35 -0
- data/lib/display/displayer.rb +81 -0
- data/lib/file_writer.rb +19 -0
- data/lib/models/article.rb +38 -0
- data/lib/models/articles.rb +40 -0
- data/lib/qiita_json_loader.rb +43 -0
- data/lib/qiita_matome/version.rb +4 -0
- data/lib/qiita_matome_core.rb +121 -0
- data/lib/qiita_matome_dsl.rb +31 -0
- data/lib/qiita_matome_dsl_model.rb +37 -0
- data/lib/sort.rb +10 -0
- data/lib/sort/sort_consts.rb +28 -0
- data/lib/sort/sorter.rb +51 -0
- data/lib/validators.rb +13 -0
- data/lib/validators/article_validator.rb +19 -0
- data/lib/validators/articles_validator.rb +16 -0
- data/lib/validators/display_columns_validator.rb +41 -0
- data/lib/validators/sort_type_validator.rb +19 -0
- data/manual_test_docs/.gitkeep +0 -0
- data/qiita_matome.gemspec +28 -0
- data/samples/matome_title_asc.md +25 -0
- data/spec/display/displayer_spec.rb +341 -0
- data/spec/file_writer_spec.rb +92 -0
- data/spec/models/article_spec.rb +131 -0
- data/spec/models/articles_spec.rb +305 -0
- data/spec/qiita_matome_core_spec.rb +46 -0
- data/spec/sort/sorter_spec.rb +392 -0
- data/spec/spec_helper.rb +16 -0
- data/spec/validators/article_validator_spec.rb +53 -0
- data/spec/validators/articles_validator_spec.rb +54 -0
- data/spec/validators/display_columns_validator_spec.rb +58 -0
- data/spec/validators/sort_type_validator_spec.rb +54 -0
- metadata +202 -0
data/lib/file_writer.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module QiitaMatome
|
4
|
+
# QiitaMatome FileWriter
|
5
|
+
class FileWriter
|
6
|
+
attr_reader :output_file, :contents
|
7
|
+
|
8
|
+
def initialize(output_file, contents)
|
9
|
+
@output_file = output_file
|
10
|
+
@contents = contents
|
11
|
+
end
|
12
|
+
|
13
|
+
def write
|
14
|
+
dir = File.dirname(@output_file)
|
15
|
+
FileUtils.mkdir_p(dir) unless Dir.exist?(dir)
|
16
|
+
File.open(@output_file, 'w:UTF-8') { |f|f.print @contents }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
3
|
+
module QiitaMatome
|
4
|
+
# QiitaMatome::Article
|
5
|
+
class Article
|
6
|
+
YMDHMS_DATETIME_FORMAT = '%Y/%m/%d %H:%M:%S'.freeze
|
7
|
+
attr_accessor :user, :title, :uuid, :created_at, :updated_at, :tags, :stock_count # rubocop:disable LineLength
|
8
|
+
|
9
|
+
# rubocop:disable MethodLength
|
10
|
+
def initialize(options = {})
|
11
|
+
@uuid = options['uuid']
|
12
|
+
@user = options['user']
|
13
|
+
@title = options['title']
|
14
|
+
@uuid = options['uuid']
|
15
|
+
c_at = options['created_at']
|
16
|
+
u_at = options['updated_at']
|
17
|
+
@created_at = DateTime.parse(c_at) unless c_at.nil?
|
18
|
+
@updated_at = DateTime.parse(u_at) unless u_at.nil?
|
19
|
+
@tags = options['tags']
|
20
|
+
@stock_count = options['stock_count']
|
21
|
+
yield(self) if block_given?
|
22
|
+
end
|
23
|
+
# rubocop:enable MethodLength
|
24
|
+
|
25
|
+
def created_at_ymdhms
|
26
|
+
@created_at.strftime(YMDHMS_DATETIME_FORMAT)
|
27
|
+
end
|
28
|
+
|
29
|
+
def updated_at_ymdhms
|
30
|
+
@updated_at.strftime(YMDHMS_DATETIME_FORMAT)
|
31
|
+
end
|
32
|
+
|
33
|
+
def title_link
|
34
|
+
url_name = user['url_name']
|
35
|
+
"[#{title.gsub('|', '')}](http://qiita.com/#{url_name}/items/#{uuid})"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'models/article'
|
2
|
+
|
3
|
+
module QiitaMatome
|
4
|
+
# QiitaMatome::Articles
|
5
|
+
class Articles
|
6
|
+
include Enumerable
|
7
|
+
attr_accessor :articles
|
8
|
+
|
9
|
+
def self.exclude_uuid(target_articles, uuids)
|
10
|
+
uuids_list = Array(uuids)
|
11
|
+
target_articles.delete_if { |e|uuids_list.include?(e.uuid) }
|
12
|
+
target_articles
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(articles = [])
|
16
|
+
@articles = articles
|
17
|
+
end
|
18
|
+
|
19
|
+
def <<(article)
|
20
|
+
@articles << article
|
21
|
+
end
|
22
|
+
|
23
|
+
def +(other)
|
24
|
+
@articles += other.articles
|
25
|
+
self
|
26
|
+
end
|
27
|
+
|
28
|
+
def size
|
29
|
+
@articles.size
|
30
|
+
end
|
31
|
+
|
32
|
+
def each
|
33
|
+
@articles.each { |article|yield(article) }
|
34
|
+
end
|
35
|
+
|
36
|
+
def filter_by_tag(tag)
|
37
|
+
@articles.select { |e|e.tags.map { |t|t['name'] }.include?(tag) }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
require 'openssl'
|
3
|
+
require 'json'
|
4
|
+
require 'models/article'
|
5
|
+
require 'models/articles'
|
6
|
+
|
7
|
+
module QiitaMatome
|
8
|
+
# QiitaMatome::QiitaJsonLoader
|
9
|
+
class QiitaJsonLoader
|
10
|
+
attr_reader :user, :articles
|
11
|
+
QIITA_URL = 'https://qiita.com/api/v1/users/%s/items?page=%s&per_page=%s'
|
12
|
+
PER_PAGE = 25
|
13
|
+
PAGE_LIMIT = 100
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
@articles = Articles.new
|
17
|
+
end
|
18
|
+
|
19
|
+
def load(user)
|
20
|
+
@user = user
|
21
|
+
tmp_verbose = $VERBOSE
|
22
|
+
$VERBOSE = nil
|
23
|
+
OpenSSL::SSL.const_set('VERIFY_PEER', OpenSSL::SSL::VERIFY_NONE)
|
24
|
+
$VERBOSE = tmp_verbose
|
25
|
+
(1..PAGE_LIMIT).each do |page|
|
26
|
+
before_size = @articles.size
|
27
|
+
load_page(user, page)
|
28
|
+
break if before_size == @articles.size
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def load_page(user, page)
|
35
|
+
json = open(format(QIITA_URL, user, page, PER_PAGE)).read
|
36
|
+
json_articles = JSON.parser.new(json).parse
|
37
|
+
@articles += json_articles.each_with_object(Articles.new) do |item, memo|
|
38
|
+
memo << Article.new(item)
|
39
|
+
memo
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'display/displayer'
|
3
|
+
require 'file_writer'
|
4
|
+
require 'models/article'
|
5
|
+
require 'models/articles'
|
6
|
+
require 'qiita_json_loader'
|
7
|
+
require 'qiita_matome_dsl'
|
8
|
+
require 'sort/sorter'
|
9
|
+
|
10
|
+
module QiitaMatome
|
11
|
+
# QiitaMatome Core
|
12
|
+
class Core
|
13
|
+
QIITA_MATOME_FILE = 'Qiitamatomefile'
|
14
|
+
|
15
|
+
# rubocop:disable LineLength
|
16
|
+
QIITA_MATOME_TEMPLATE = <<-EOS
|
17
|
+
# encoding: utf-8
|
18
|
+
|
19
|
+
# Set your qiita user name
|
20
|
+
# user is required
|
21
|
+
# user allow only String
|
22
|
+
user "your value"
|
23
|
+
|
24
|
+
# Set your matome target tag name
|
25
|
+
# tag is required
|
26
|
+
# tag allow only String
|
27
|
+
tag "your value"
|
28
|
+
|
29
|
+
# Set your matome title
|
30
|
+
# title is required
|
31
|
+
# title allow only String
|
32
|
+
title "your value"
|
33
|
+
|
34
|
+
# Set your matome file's output path
|
35
|
+
# output_file is required
|
36
|
+
# output_file allow only String
|
37
|
+
# output_file's default value => "matome.md"
|
38
|
+
output_file "matome.md"
|
39
|
+
|
40
|
+
# Set your matome sort type. you can choose created_at_asc/desc, updated_at_asc/desc, title_asc/desc, stock_count_asc/desc
|
41
|
+
# sort_type allow only String
|
42
|
+
# sort_type's default value => "created_at_desc"
|
43
|
+
sort_type "created_at_desc"
|
44
|
+
|
45
|
+
# Set your matome display columns. you can choose :title, :created_at, :updated_at, :stock_count and :no
|
46
|
+
# display_columns allow only Array
|
47
|
+
# display_columns's default value => [:no, :title, :created_at, :stock_count]
|
48
|
+
display_columns [:no, :title, :created_at, :stock_count]
|
49
|
+
|
50
|
+
# Set your matome exclude files
|
51
|
+
# excludes allow only Array[String, String ...]
|
52
|
+
# String is uuid. For example, 'edbfecb6a6789dd54f47'
|
53
|
+
# excludes's default value => []
|
54
|
+
excludes []
|
55
|
+
EOS
|
56
|
+
# rubocop:enable LineLength
|
57
|
+
|
58
|
+
# Generate Qiitamatomefile to current directory.
|
59
|
+
def init
|
60
|
+
File.open(QIITA_MATOME_FILE, 'w') { |f|f.puts QIITA_MATOME_TEMPLATE }
|
61
|
+
end
|
62
|
+
|
63
|
+
# Generate QiitaMatome markdown file.
|
64
|
+
def matome
|
65
|
+
dsl = read_dsl
|
66
|
+
articles = read_articles(dsl)
|
67
|
+
excluded_articles = read_excluded_articles(dsl, articles)
|
68
|
+
sorted_articles = read_sorted_articles(dsl, excluded_articles)
|
69
|
+
title = dsl.qiita_matome.title
|
70
|
+
display_columns = dsl.qiita_matome.display_columns
|
71
|
+
markdown = read_markdown(sorted_articles, title, display_columns)
|
72
|
+
output_file = dsl.qiita_matome.output_file
|
73
|
+
fw = QiitaMatome::FileWriter.new(output_file, markdown)
|
74
|
+
fw.write
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def read_dsl
|
80
|
+
src = File.open(QIITA_MATOME_FILE) { |f|f.read }
|
81
|
+
dsl = QiitaMatome::Dsl.new
|
82
|
+
dsl.instance_eval src
|
83
|
+
dsl
|
84
|
+
end
|
85
|
+
|
86
|
+
def read_articles(dsl)
|
87
|
+
user = dsl.qiita_matome.user
|
88
|
+
qjl = QiitaJsonLoader.new
|
89
|
+
qjl.load(user)
|
90
|
+
qjl.articles
|
91
|
+
end
|
92
|
+
|
93
|
+
def read_excluded_articles(dsl, articles)
|
94
|
+
tag = dsl.qiita_matome.tag
|
95
|
+
filterd_articles = articles.filter_by_tag(tag)
|
96
|
+
uuids = dsl.qiita_matome.excludes
|
97
|
+
Articles.exclude_uuid(filterd_articles, uuids)
|
98
|
+
end
|
99
|
+
|
100
|
+
def read_sorted_articles(dsl, articles)
|
101
|
+
sort_type = dsl.qiita_matome.sort_type
|
102
|
+
sorter = Sort::Sorter.new(articles, sort_type)
|
103
|
+
sorter.sort
|
104
|
+
end
|
105
|
+
|
106
|
+
def read_markdown(articles, title, display_columns)
|
107
|
+
dd = Display::Displayer.new(title, articles, display_columns)
|
108
|
+
display_title = dd.display_title
|
109
|
+
matome_updated = dd.matome_updated
|
110
|
+
table_header = dd.table_header
|
111
|
+
display_articles = dd.display_articles
|
112
|
+
<<-EOS
|
113
|
+
#{display_title}
|
114
|
+
|
115
|
+
#{matome_updated}
|
116
|
+
|
117
|
+
#{table_header}#{display_articles}
|
118
|
+
EOS
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'qiita_matome_dsl_model'
|
3
|
+
|
4
|
+
module QiitaMatome
|
5
|
+
# Dsl
|
6
|
+
class Dsl
|
7
|
+
attr_accessor :qiita_matome
|
8
|
+
|
9
|
+
# String Define
|
10
|
+
[:user, :tag, :title, :output_file, :sort_type].each do |f|
|
11
|
+
define_method f do |value|
|
12
|
+
@qiita_matome.send("#{f}=", value)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# Array/Hash/Boolean Define
|
17
|
+
[:display_columns, :excludes].each do |f|
|
18
|
+
define_method f do |value|
|
19
|
+
@qiita_matome.send("#{f}=", value)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize
|
24
|
+
@qiita_matome = QiitaMatome::DslModel.new
|
25
|
+
@qiita_matome.output_file = 'matome.md'
|
26
|
+
@qiita_matome.sort_type = 'created_at_desc'
|
27
|
+
@qiita_matome.display_columns = [:no, :title, :create_date, :stocked]
|
28
|
+
@qiita_matome.excludes = []
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'active_model'
|
3
|
+
|
4
|
+
module QiitaMatome
|
5
|
+
# DslModel
|
6
|
+
class DslModel
|
7
|
+
include ActiveModel::Model
|
8
|
+
|
9
|
+
# Set your qiita user name
|
10
|
+
attr_accessor :user
|
11
|
+
validates :user, presence: true
|
12
|
+
|
13
|
+
# Set your matome target tag name
|
14
|
+
attr_accessor :tag
|
15
|
+
validates :tag, presence: true
|
16
|
+
|
17
|
+
# Set your matome title
|
18
|
+
attr_accessor :title
|
19
|
+
validates :title, presence: true
|
20
|
+
|
21
|
+
# Set your matome file's output path
|
22
|
+
attr_accessor :output_file
|
23
|
+
validates :output_file, presence: true
|
24
|
+
|
25
|
+
# Set your matome sort type.
|
26
|
+
# you can choose created_at_asc/desc, updated_at_asc/desc,
|
27
|
+
# title_asc/desc, stock_count_asc/desc
|
28
|
+
attr_accessor :sort_type
|
29
|
+
|
30
|
+
# Set your matome display columns.
|
31
|
+
# you can choose :title, :create_date, :update_date, :stocked and :no
|
32
|
+
attr_accessor :display_columns
|
33
|
+
|
34
|
+
# Set your matome exclude files
|
35
|
+
attr_accessor :excludes
|
36
|
+
end
|
37
|
+
end
|
data/lib/sort.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module QiitaMatome
|
4
|
+
# QiitaMatome::Sort
|
5
|
+
module Sort
|
6
|
+
# QiitaMatome::Sort::Consts
|
7
|
+
module Consts
|
8
|
+
CREATED_AT_ASC = 'created_at_asc'.freeze
|
9
|
+
CREATED_AT_DESC = 'created_at_desc'.freeze
|
10
|
+
UPDATED_AT_ASC = 'updated_at_asc'.freeze
|
11
|
+
UPDATED_AT_DESC = 'updated_at_desc'.freeze
|
12
|
+
TITLE_ASC = 'title_asc'.freeze
|
13
|
+
TITLE_DESC = 'title_desc'.freeze
|
14
|
+
STOCK_COUNT_ASC = 'stock_count_asc'.freeze
|
15
|
+
STOCK_COUNT_DESC = 'stock_count_desc'.freeze
|
16
|
+
ALL_TYPES = [
|
17
|
+
CREATED_AT_ASC,
|
18
|
+
CREATED_AT_DESC,
|
19
|
+
UPDATED_AT_ASC,
|
20
|
+
UPDATED_AT_DESC,
|
21
|
+
TITLE_ASC,
|
22
|
+
TITLE_DESC,
|
23
|
+
STOCK_COUNT_ASC,
|
24
|
+
STOCK_COUNT_DESC
|
25
|
+
]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/sort/sorter.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'sort/sort_consts'
|
3
|
+
require 'validators/article_validator'
|
4
|
+
require 'validators/articles_validator'
|
5
|
+
require 'validators/sort_type_validator'
|
6
|
+
|
7
|
+
module QiitaMatome
|
8
|
+
# QiitaMatome::Sort
|
9
|
+
module Sort
|
10
|
+
# QiitaMatome::Sort::Sorter
|
11
|
+
class Sorter
|
12
|
+
attr_reader :articles, :sort_type
|
13
|
+
|
14
|
+
# rubocop:disable LineLength
|
15
|
+
SORT_PATTERNS = {
|
16
|
+
Consts::CREATED_AT_ASC => { send_method: :sort_asc, sort_key: :created_at },
|
17
|
+
Consts::CREATED_AT_DESC => { send_method: :sort_desc, sort_key: :created_at },
|
18
|
+
Consts::UPDATED_AT_ASC => { send_method: :sort_asc, sort_key: :updated_at },
|
19
|
+
Consts::UPDATED_AT_DESC => { send_method: :sort_desc, sort_key: :updated_at },
|
20
|
+
Consts::TITLE_ASC => { send_method: :sort_asc, sort_key: :title },
|
21
|
+
Consts::TITLE_DESC => { send_method: :sort_desc, sort_key: :title },
|
22
|
+
Consts::STOCK_COUNT_ASC => { send_method: :sort_asc, sort_key: :stock_count },
|
23
|
+
Consts::STOCK_COUNT_DESC => { send_method: :sort_desc, sort_key: :stock_count }
|
24
|
+
}
|
25
|
+
# rubocop:enable LineLength
|
26
|
+
|
27
|
+
def initialize(articles, sort_type = Consts::UPDATED_AT_DESC)
|
28
|
+
Validators::ArticlesValidator.validate(articles)
|
29
|
+
Validators::ArticleValidator.validate(articles)
|
30
|
+
Validators::SortTypeValidator.validate(sort_type)
|
31
|
+
@articles = articles
|
32
|
+
@sort_type = sort_type
|
33
|
+
end
|
34
|
+
|
35
|
+
def sort
|
36
|
+
sort_pattern = SORT_PATTERNS[@sort_type]
|
37
|
+
send(sort_pattern[:send_method], sort_pattern[:sort_key])
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def sort_asc(asc_key)
|
43
|
+
@articles.sort_by! { |e|e.send(asc_key) }
|
44
|
+
end
|
45
|
+
|
46
|
+
def sort_desc(desc_key)
|
47
|
+
@articles = @articles.sort_by { |e|e.send(desc_key) }.reverse
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/lib/validators.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'validators/article_validator'
|
4
|
+
require 'validators/articles_validator'
|
5
|
+
require 'validators/display_columns_validator'
|
6
|
+
require 'validators/sort_type_validator'
|
7
|
+
require 'validators/sort_type_validator'
|
8
|
+
|
9
|
+
module QiitaMatome
|
10
|
+
# QiitaMatome::Validators
|
11
|
+
module Validators
|
12
|
+
end
|
13
|
+
end
|