qiita_matome 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +1 -0
  3. data/.gitignore +22 -0
  4. data/.rspec +2 -0
  5. data/.rubocop.yml +0 -0
  6. data/.travis.yml +8 -0
  7. data/CONTRIBUTING.md +44 -0
  8. data/Gemfile +13 -0
  9. data/LICENSE.txt +22 -0
  10. data/README.md +210 -0
  11. data/Rakefile +8 -0
  12. data/bin/qiitam +34 -0
  13. data/lib/display.rb +10 -0
  14. data/lib/display/display_consts.rb +35 -0
  15. data/lib/display/displayer.rb +81 -0
  16. data/lib/file_writer.rb +19 -0
  17. data/lib/models/article.rb +38 -0
  18. data/lib/models/articles.rb +40 -0
  19. data/lib/qiita_json_loader.rb +43 -0
  20. data/lib/qiita_matome/version.rb +4 -0
  21. data/lib/qiita_matome_core.rb +121 -0
  22. data/lib/qiita_matome_dsl.rb +31 -0
  23. data/lib/qiita_matome_dsl_model.rb +37 -0
  24. data/lib/sort.rb +10 -0
  25. data/lib/sort/sort_consts.rb +28 -0
  26. data/lib/sort/sorter.rb +51 -0
  27. data/lib/validators.rb +13 -0
  28. data/lib/validators/article_validator.rb +19 -0
  29. data/lib/validators/articles_validator.rb +16 -0
  30. data/lib/validators/display_columns_validator.rb +41 -0
  31. data/lib/validators/sort_type_validator.rb +19 -0
  32. data/manual_test_docs/.gitkeep +0 -0
  33. data/qiita_matome.gemspec +28 -0
  34. data/samples/matome_title_asc.md +25 -0
  35. data/spec/display/displayer_spec.rb +341 -0
  36. data/spec/file_writer_spec.rb +92 -0
  37. data/spec/models/article_spec.rb +131 -0
  38. data/spec/models/articles_spec.rb +305 -0
  39. data/spec/qiita_matome_core_spec.rb +46 -0
  40. data/spec/sort/sorter_spec.rb +392 -0
  41. data/spec/spec_helper.rb +16 -0
  42. data/spec/validators/article_validator_spec.rb +53 -0
  43. data/spec/validators/articles_validator_spec.rb +54 -0
  44. data/spec/validators/display_columns_validator_spec.rb +58 -0
  45. data/spec/validators/sort_type_validator_spec.rb +54 -0
  46. metadata +202 -0
@@ -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,4 @@
1
+ # QiitaMatome
2
+ module QiitaMatome
3
+ VERSION = '0.0.1'
4
+ 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
@@ -0,0 +1,10 @@
1
+ # encoding: utf-8
2
+
3
+ require 'sort/sorter'
4
+ require 'sort/sort_consts'
5
+
6
+ module QiitaMatome
7
+ # QiitaMatome::Sort
8
+ module Sort
9
+ end
10
+ end
@@ -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
@@ -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
@@ -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