hatenablog 0.5.1 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: ccc4d78dfaffced49de2aa8f7e9794d2767302a9
4
- data.tar.gz: 4edfad9e3555d2fecf0726049864ced76e8b5b48
2
+ SHA256:
3
+ metadata.gz: 5c6ebd627cfd566a53f9423b632b3ef6f42d285e2ffa84b176af438022a78bbe
4
+ data.tar.gz: c7dbd064aee0d3557c17464be93b351cc741b973c16ede5dd6d8cf6afde30bfc
5
5
  SHA512:
6
- metadata.gz: dee4dd74034022f4f559ce6c24fbeb7dcaa9853451d4db59cbfbf4c85effc3c9b1a55b3434978a11011be802aef9355e3b774c5f6eca77fe558e8f2011ed00f6
7
- data.tar.gz: fe01c44cfcc8b547ea7f69791704ac7c8da279cf701422354e5f151bf9e10a4501089da6db20e3740e6a0f4ba449110d3d6ff6128caf5312e547c7d6a628b283
6
+ metadata.gz: fe094c01e6c51c1b41bd674bc6aa891767532c3472135312709d090bd1301dfcc3041fb95e90a7a641c20ea07f5a32403d6d006a13a8a6c16aa319c74ad1c454
7
+ data.tar.gz: 8c86b99f15106170419012c8e8d15c90b1a2644488a62b16cfb76defad288ca81cb04a920f772d8fedf314f88ff8c4351889439a766e59305caad5134d0d15cf
@@ -0,0 +1,49 @@
1
+ name: build
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - master
7
+ pull_request:
8
+
9
+ jobs:
10
+ build:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ ruby: ['2.6', '2.7', '3.0', '3.1.0-preview1']
15
+ include:
16
+ - ruby: '3.0'
17
+ report-coverage: true
18
+ steps:
19
+ - uses: actions/checkout@v2
20
+ - uses: ruby/setup-ruby@v1
21
+ with:
22
+ ruby-version: ${{ matrix.ruby }}
23
+ - run: gem install bundler
24
+ - run: bundle install -j4
25
+ - name: Run tests and report test coverage
26
+ if: matrix.report-coverage
27
+ uses: paambaati/codeclimate-action@v2.7.5
28
+ env:
29
+ CC_TEST_REPORTER_ID: 309cf0784d00d2a6009566d28be111a8a0280cdeb2da280225eedf577b16beb5
30
+ with:
31
+ coverageCommand: bundle exec rake
32
+ coverageLocations: ${{github.workspace}}/coverage/coverage.json:simplecov
33
+ - name: Run tests
34
+ if: "!matrix.report-coverage"
35
+ env:
36
+ TZ: Asia/Tokyo
37
+ run: bundle exec rake
38
+ steep:
39
+ runs-on: ubuntu-latest
40
+ steps:
41
+ - uses: actions/checkout@v2
42
+ with:
43
+ submodules: 'true'
44
+ - uses: ruby/setup-ruby@v1
45
+ with:
46
+ ruby-version: '3.0'
47
+ - run: bundle install -j4
48
+ - run: rbs collection install
49
+ - run: bundle exec steep check
data/.gitignore CHANGED
@@ -28,9 +28,10 @@ build/
28
28
 
29
29
  # for a library or gem, you might want to ignore these files since the code is
30
30
  # intended to run in multiple environments; otherwise, check them in:
31
- # Gemfile.lock
32
- # .ruby-version
33
- # .ruby-gemset
31
+ Gemfile.lock
32
+ .ruby-version
33
+ .ruby-gemset
34
34
 
35
35
  # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
36
36
  .rvmrc
37
+ /.gem_rbs_collection/
data/CHANGELOG.md ADDED
@@ -0,0 +1,21 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## Unreleased
8
+
9
+ ## 0.8.0 - 2021-11-20
10
+
11
+ ### Added
12
+
13
+ - [Create the signature with RBS](https://github.com/kymmt90/hatenablog/pull/24)
14
+ - This is experimental
15
+
16
+ ### Changed
17
+
18
+ - [Change the executable name to more specific one](https://github.com/kymmt90/hatenablog/pull/26)
19
+ - From `get_access_token` to `get_hatena_oauth_access_token`
20
+ - [Use only supported Rubies in 2021-11](https://github.com/kymmt90/hatenablog/pull/27)
21
+ - Drop Ruby 2.4 and 2.5 and add Ruby 3.1.0-preview1
data/Gemfile CHANGED
@@ -1,8 +1,11 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
+ group :development, :test do
4
+ gem 'steep'
5
+ end
6
+
3
7
  group :test do
4
8
  gem 'simplecov'
5
- gem 'codeclimate-test-reporter', '~> 1.0.0'
6
9
  end
7
10
 
8
11
  # Specify your gem's dependencies in hatenablog.gemspec
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Hatenablog
2
2
 
3
- [![Gem Version](https://badge.fury.io/rb/hatenablog.svg)](https://badge.fury.io/rb/hatenablog) [![Build Status](https://travis-ci.org/kymmt90/hatenablog.svg?branch=master)](https://travis-ci.org/kymmt90/hatenablog)
3
+ [![Gem Version](https://badge.fury.io/rb/hatenablog.svg)](https://badge.fury.io/rb/hatenablog) [![Build Status](https://github.com/kymmt90/hatenablog/workflows/build/badge.svg)](https://github.com/kymmt90/hatenablog/actions?workflow=build)
4
4
  [![Code Climate](https://codeclimate.com/github/kymmt90/hatenablog/badges/gpa.svg)](https://codeclimate.com/github/kymmt90/hatenablog)
5
5
  [![Test Coverage](https://codeclimate.com/github/kymmt90/hatenablog/badges/coverage.svg)](https://codeclimate.com/github/kymmt90/hatenablog/coverage)
6
6
 
@@ -19,15 +19,21 @@ This gem supports following operations through OAuth 1.0a or Basic authenticatio
19
19
 
20
20
  Add this line to your application's Gemfile:
21
21
 
22
- gem 'hatenablog'
22
+ ```
23
+ gem 'hatenablog'
24
+ ```
23
25
 
24
26
  And then execute:
25
27
 
26
- $ bundle
28
+ ```
29
+ $ bundle
30
+ ```
27
31
 
28
32
  Or install it yourself as:
29
33
 
30
- $ gem install hatenablog
34
+ ```
35
+ $ gem install hatenablog
36
+ ```
31
37
 
32
38
  ### Get OAuth credentials
33
39
 
@@ -41,11 +47,13 @@ Access [Hatena application registoration page](http://developer.hatena.ne.jp/) a
41
47
 
42
48
  Execute this command:
43
49
 
44
- $ get_access_token <your consumer key> <your consumer secret>
45
- Visit this website and get the PIN: https://www.hatena.com/oauth/authorize?oauth_token=XXXXXXXXXXXXXXXXXXXX
46
- Enter the PIN: <your PIN> [Enter]
47
- Access token: <your access token>
48
- Access token secret: <your access token secret>
50
+ ```
51
+ $ get_hatena_oauth_access_token <your consumer key> <your consumer secret>
52
+ Visit this website and get the PIN: https://www.hatena.com/oauth/authorize?oauth_token=XXXXXXXXXXXXXXXXXXXX
53
+ Enter the PIN: <your PIN> [Enter]
54
+ Access token: <your access token>
55
+ Access token secret: <your access token secret>
56
+ ```
49
57
 
50
58
  #### 3. [Optional] Set up the YAML configuration file
51
59
 
@@ -99,15 +107,19 @@ Hatenablog::Client.create do |blog|
99
107
  end
100
108
 
101
109
  # Post new entry
102
- posted_entry = blog.post_entry('Entry Title',
103
- 'This is entry contents', # markdown form
104
- ['Test', 'Programming']) # categories
110
+ posted_entry = blog.post_entry(
111
+ 'Entry Title',
112
+ 'This is entry contents', # markdown form
113
+ ['Test', 'Programming'] # categories
114
+ )
105
115
 
106
116
  # Update entry
107
- updated_entry = blog.update_entry(posted_entry.id,
108
- 'Revised Entry Title',
109
- posted_entry.content,
110
- posted_entry.categories)
117
+ updated_entry = blog.update_entry(
118
+ posted_entry.id,
119
+ 'Revised Entry Title',
120
+ posted_entry.content,
121
+ posted_entry.categories
122
+ )
111
123
 
112
124
  # Delete entry
113
125
  blog.delete_entry(updated_entry.id)
@@ -148,8 +160,8 @@ end
148
160
  ### Blog
149
161
 
150
162
  ```ruby
151
- client.title # Get the blog title
152
- client.author_name # Get the blog author name
163
+ client.title # Get the blog title
164
+ client.author_name # Get the blog author name
153
165
  ```
154
166
 
155
167
  ### Feeds
@@ -157,16 +169,16 @@ client.author_name # Get the blog author name
157
169
  ```ruby
158
170
  feed = client.next_feed # Get the first feed when no argument is passed
159
171
  feed.uri
160
- feed.next_uri # The next feed URI
172
+ feed.next_uri # The next feed URI
161
173
  feed.title
162
174
  feed.author_name
163
- feed.update # Updated datetime
175
+ feed.updated # Updated datetime
164
176
 
165
- feed.entries # entries in the feed
177
+ feed.entries # entries in the feed
166
178
  feed.each_entry do |entry|
167
179
  # ...
168
180
  end
169
- feed.has_next? # true if the next page exists
181
+ feed.has_next? # true if the next page exists
170
182
  next_feed = client.next_feed(feed)
171
183
  ```
172
184
 
@@ -174,38 +186,42 @@ next_feed = client.next_feed(feed)
174
186
 
175
187
  ```ruby
176
188
  client.get_entry('0000000000000000000') # Get the entry specifed by its ID
177
- client.entries # Get blog entries in the first page
178
- client.entries(1) # Get blog entries in the first and the second page
179
- client.all_entries # Get all entries in the blog
180
-
181
- entry = client.post_entry('Example Title', # title
182
- 'This is the **example** entry.', # content
183
- ['Ruby', 'Rails'], # categories
184
- 'yes' # draft
185
- )
189
+ client.entries # Get blog entries in the first page
190
+ client.entries(1) # Get blog entries in the first and the second page
191
+ client.all_entries # Get all entries in the blog
192
+
193
+ entry = client.post_entry(
194
+ 'Example Title', # title
195
+ 'This is the **example** entry.', # content
196
+ ['Ruby', 'Rails'], # categories
197
+ 'yes' # draft
198
+ )
186
199
  entry.id
187
200
  entry.uri
188
201
  entry.edit_uri
189
202
  entry.author_name
190
- entry.title #=> 'Example Title'
191
- entry.content #=> 'This is the **example** entry.'
192
- entry.draft #=> 'yes'
193
- entry.draft? #=> true
194
- entry.categories #=> ['Ruby', 'Rails']
195
- entry.updated # Updated datetime
196
-
197
- client.update_entry(entry.id,
198
- entry.title,
199
- 'This is the **modified** example entry.',
200
- entry.categories,
201
- 'no')
203
+ entry.title #=> 'Example Title'
204
+ entry.content #=> 'This is the **example** entry.'
205
+ entry.formatted_content #=> '<p>This is the <strong>example</strong> entry.</p>'
206
+ entry.draft #=> 'yes'
207
+ entry.draft? #=> true
208
+ entry.categories #=> ['Ruby', 'Rails']
209
+ entry.updated # Updated datetime
210
+
211
+ client.update_entry(
212
+ entry.id,
213
+ entry.title,
214
+ 'This is the **modified** example entry.',
215
+ entry.categories,
216
+ 'no'
217
+ )
202
218
  client.delete_entry(entry.id)
203
219
  ```
204
220
 
205
221
  ### Categories
206
222
 
207
223
  ```ruby
208
- categories = client.categories # Get categories registered in the blog
224
+ categories = client.categories # Get categories registered in the blog
209
225
  categories.each do |cat|
210
226
  puts cat
211
227
  end
data/Rakefile CHANGED
@@ -11,6 +11,7 @@ Rake::TestTask.new do |t|
11
11
  t.libs << "test"
12
12
  t.test_files = Dir["test/hatenablog/*_test.rb"]
13
13
  t.verbose = true
14
+ t.warning = true
14
15
  end
15
16
 
16
17
  YARD::Rake::YardocTask.new do |t|
data/Steepfile ADDED
@@ -0,0 +1,11 @@
1
+ D = Steep::Diagnostic
2
+
3
+ target :lib do
4
+ check "lib"
5
+ signature "sig"
6
+
7
+ repo_path ".gem_rbs_collection"
8
+ library "erb", "net-http", "nokogiri", "set", "time", "uri"
9
+
10
+ configure_code_diagnostics(D::Ruby.all_error)
11
+ end
@@ -10,6 +10,7 @@ class AccessTokenGetter
10
10
  SITE_URI = 'https://www.hatena.com'
11
11
  REQUEST_TOKEN_URI = '/oauth/initiate?scope=read_public%2Cread_private%2Cwrite_public%2Cwrite_private'
12
12
  ACCESS_TOKEN_URI = '/oauth/token'
13
+ AUTHORIZE_URI = 'https://www.hatena.ne.jp/oauth/authorize'
13
14
 
14
15
  def initialize(consumer_key, consumer_secret)
15
16
  @consumer_key = consumer_key
@@ -19,7 +20,8 @@ class AccessTokenGetter
19
20
  oauth_callback: 'oob',
20
21
  site: SITE_URI,
21
22
  request_token_url: REQUEST_TOKEN_URI,
22
- access_token_url: ACCESS_TOKEN_URI)
23
+ access_token_url: ACCESS_TOKEN_URI,
24
+ authorize_url: AUTHORIZE_URI)
23
25
  end
24
26
 
25
27
  def get_request_token
data/hatenablog.gemspec CHANGED
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
19
19
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
20
  spec.require_paths = ["lib"]
21
21
 
22
- spec.required_ruby_version = '>= 2.2'
22
+ spec.required_ruby_version = '>= 2.3'
23
23
 
24
24
  spec.add_development_dependency "bundler"
25
25
  spec.add_development_dependency "rake"
@@ -15,9 +15,12 @@ module Hatenablog
15
15
  @categories.dup
16
16
  end
17
17
 
18
- def each
18
+ def each(&block)
19
+ return enum_for unless block_given?
20
+
19
21
  @categories.each do |category|
20
- yield category
22
+ # @type var block: ^(String) -> void
23
+ block.call(category)
21
24
  end
22
25
  end
23
26
 
@@ -36,7 +39,7 @@ module Hatenablog
36
39
  end
37
40
 
38
41
  def parse_document
39
- @categories = @document.css('atom|category').inject([]) do |categories, category|
42
+ @categories = @document.css('atom|category').inject(Array.new) do |categories, category|
40
43
  categories << category['term'].to_s
41
44
  end
42
45
 
@@ -12,6 +12,7 @@ module Hatenablog
12
12
  MEMBER_URI = "https://blog.hatena.ne.jp/%s/%s/atom/entry/%s".freeze
13
13
  CATEGORY_URI = "https://blog.hatena.ne.jp/%s/%s/atom/category".freeze
14
14
 
15
+ # @dynamic requester=
15
16
  attr_writer :requester
16
17
 
17
18
  # Create a new hatenablog AtomPub client from a configuration file.
@@ -24,9 +25,9 @@ module Hatenablog
24
25
  yield blog
25
26
  end
26
27
 
27
- def initialize(config = nil)
28
+ def initialize(config = Configuration.new)
28
29
  if block_given?
29
- yield config = Configuration.new
30
+ yield config
30
31
  config.check_valid_or_raise
31
32
  end
32
33
  @requester = Requester.create(config)
@@ -59,7 +60,7 @@ module Hatenablog
59
60
  # Get all blog entries.
60
61
  # @return [Hatenablog::Entries] enumerator of blog entries
61
62
  def all_entries
62
- Entries.new(self, nil)
63
+ Entries.new(self, 0, :all)
63
64
  end
64
65
 
65
66
  # Get the next feed of the given feed.
@@ -161,7 +162,7 @@ module Hatenablog
161
162
  xml.name author_name
162
163
  end
163
164
  xml.content(content, type: 'text/x-markdown')
164
- xml.updated updated unless updated.empty? || updated.nil?
165
+ xml.updated updated if updated && !updated.empty?
165
166
  categories.each do |category|
166
167
  xml.category(term: category)
167
168
  end
@@ -207,17 +208,45 @@ module Hatenablog
207
208
  class Entries
208
209
  include Enumerable
209
210
 
210
- def initialize(client, page = 0)
211
+ def initialize(client, page = 0, fetch = :partial)
211
212
  @client = client
212
213
  @page = page
214
+ @fetch = fetch
213
215
  end
214
216
 
215
- def each
217
+ def each(&block)
218
+ return enum_for unless block_given?
219
+
220
+ # @type var block: ^(Entry) -> void
221
+ if @fetch == :all
222
+ each_all(&block)
223
+ else
224
+ each_partial(&block)
225
+ end
226
+ end
227
+
228
+ private
229
+
230
+ def each_all(&block)
231
+ feed = nil
232
+
233
+ while feed = @client.next_feed(feed)
234
+ feed.entries.each { |entry| block.call(entry) }
235
+ end
236
+
237
+ self
238
+ end
239
+
240
+ def each_partial(&block)
241
+ feed = nil
242
+
216
243
  current_page = 0
217
- until (@page && current_page > @page) || !(feed = @client.next_feed(feed))
218
- feed.entries.each { |entry| yield entry }
244
+ while current_page <= @page && feed = @client.next_feed(feed)
245
+ feed.entries.each { |entry| block.call(entry) }
219
246
  current_page += 1
220
247
  end
248
+
249
+ self
221
250
  end
222
251
  end
223
252
  end
@@ -4,6 +4,8 @@ require 'ostruct'
4
4
 
5
5
  module Hatenablog
6
6
  class Configuration < OpenStruct
7
+ # @dynamic auth_type, consumer_key, consumer_secret, access_token, access_token_secret, api_key, user_id, blog_id
8
+
7
9
  OAUTH_KEYS = %i(consumer_key consumer_secret access_token access_token_secret user_id blog_id)
8
10
  BASIC_KEYS = %i(api_key user_id blog_id)
9
11
 
@@ -3,6 +3,9 @@ require 'time'
3
3
 
4
4
  module Hatenablog
5
5
  module AfterHook
6
+ # @dynamic uri=, edit_uri=, author_name=, title=, content=, updated=, draft=, categories=
7
+ # @dynamic instance_methods, alias_method, define_method
8
+
6
9
  # Register a hooking method for given methods.
7
10
  # The hook method is executed after calling given methods.
8
11
  # @param [Symbol] hooking method name
@@ -17,8 +20,10 @@ module Hatenablog
17
20
  alias_method origin_method, method
18
21
 
19
22
  define_method(method) do |*args, &block|
23
+ # @type var block: ^(*untyped) -> untyped
20
24
  result = send(origin_method, *args, &block)
21
25
  send(hook)
26
+ result
22
27
  end
23
28
  end
24
29
  end
@@ -27,8 +32,14 @@ module Hatenablog
27
32
  class Entry
28
33
  extend AfterHook
29
34
 
30
- attr_accessor :uri, :author_name, :title, :content, :draft, :categories
31
- attr_reader :edit_uri, :id, :updated
35
+ # @dynamic uri, uri=, author_name, author_name=, title, title=, content, content=, draft, draft=
36
+ attr_accessor :uri, :author_name, :title, :content, :draft
37
+
38
+ # @dynamic edit_uri, id, updated
39
+ attr_reader :edit_uri, :id, :updated
40
+
41
+ # @dynamic categories=
42
+ attr_writer :categories
32
43
 
33
44
  def updated=(date)
34
45
  @updated = Time.parse(date)
@@ -87,6 +98,11 @@ module Hatenablog
87
98
  @document.to_s.gsub(/\"/, "'")
88
99
  end
89
100
 
101
+ # @return [String]
102
+ def formatted_content
103
+ @formatted_content
104
+ end
105
+
90
106
  def self.build_xml(uri, edit_uri, author_name, title, content, draft, categories, updated)
91
107
  builder = Nokogiri::XML::Builder.new(encoding: 'utf-8') do |xml|
92
108
  xml.entry('xmlns' => 'http://www.w3.org/2005/Atom',
@@ -128,15 +144,19 @@ module Hatenablog
128
144
  @author_name = @document.at_css('author name').content
129
145
  @title = @document.at_css('title').content
130
146
  @content = @document.at_css('content').content
147
+ @formatted_content = @document.xpath('//hatena:formatted-content', hatena: 'http://www.hatena.ne.jp/info/xmlns#')[0]
148
+ @formatted_content = @formatted_content.content if @formatted_content
131
149
  @draft = @document.at_css('entry app|control app|draft').content
132
150
  @categories = parse_categories
133
- unless @document.at_css('entry updated').nil?
151
+ if @document.at_css('entry updated')
134
152
  @updated = Time.parse(@document.at_css('entry updated').content)
153
+ else
154
+ @updated = nil
135
155
  end
136
156
  end
137
157
 
138
158
  def parse_categories
139
- categories = @document.css('category').inject([]) do |categories, category|
159
+ categories = @document.css('category').inject(Array.new) do |categories, category|
140
160
  categories << category['term'].to_s
141
161
  end
142
162
  categories
@@ -151,7 +171,7 @@ module Hatenablog
151
171
  @document.at_css('entry app|control app|draft').content = @draft
152
172
 
153
173
  unless @updated.nil? || @document.at_css('entry updated').nil?
154
- @document.at_css('entry updated').content = @updated.iso8601
174
+ @document.at_css('entry updated').content = @updated&.iso8601
155
175
  end
156
176
 
157
177
  unless @categories.nil?
@@ -5,6 +5,7 @@ require 'hatenablog/entry'
5
5
 
6
6
  module Hatenablog
7
7
  class Feed
8
+ # @dynamic uri, next_uri, title, author_name, updated
8
9
  attr_reader :uri, :next_uri, :title, :author_name, :updated
9
10
 
10
11
  # Create a new blog feed from a XML string.
@@ -53,7 +54,7 @@ module Hatenablog
53
54
  end
54
55
 
55
56
  def parse_entry
56
- @entries = @document.css('feed > entry').inject([]) do |entries, entry|
57
+ @entries = @document.css('feed > entry').inject(Array.new) do |entries, entry|
57
58
  # add namespace 'app' to recognize XML correctly
58
59
  entry['xmlns:app'] = 'http://www.w3.org/2007/app'
59
60
  entries << Hatenablog::Entry.load_xml(entry.to_s)
@@ -96,7 +96,7 @@ module Hatenablog
96
96
  # @param [string] body HTTP request body
97
97
  # @param [string] headers HTTP request headers
98
98
  # @return [Net::HTTPResponse] HTTP response
99
- def post(uri, body, headers = nil)
99
+ def post(uri, body, headers = {})
100
100
  request(uri, :post, body: body, headers: headers)
101
101
  end
102
102
 
@@ -105,7 +105,7 @@ module Hatenablog
105
105
  # @param [string] body HTTP request body
106
106
  # @param [string] headers HTTP request headers
107
107
  # @return [Net::HTTPResponse] HTTP response
108
- def put(uri, body, headers = nil )
108
+ def put(uri, body, headers = {})
109
109
  request(uri, :put, body: body, headers: headers)
110
110
  end
111
111
 
@@ -113,22 +113,27 @@ module Hatenablog
113
113
  # @param [string] uri target URI
114
114
  # @param [string] headers HTTP request headers
115
115
  # @return [Net::HTTPResponse] HTTP response
116
- def delete(uri, headers = nil)
116
+ def delete(uri, headers = {})
117
117
  request(uri, :delete, headers: headers)
118
118
  end
119
119
 
120
120
  private
121
- def request(uri, method, body: nil, headers: nil)
121
+
122
+ def request(uri, method, body: nil, headers: {})
122
123
  uri = URI(uri)
123
- req = METHODS[method].new(uri, headers)
124
+ req = METHODS[method].new(uri.to_s, headers)
124
125
  req.basic_auth @user_id, @api_key
125
126
  if body
126
127
  req.body = body
127
128
  req.content_type = ATOM_CONTENT_TYPE
128
129
  end
129
130
 
130
- http = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.port == 443)
131
- http.request(req)
131
+ http = Net::HTTP.new(uri.hostname, uri.port)
132
+ http.use_ssl = uri.port == 443
133
+ http.start do |conn|
134
+ conn.request(req)
135
+ end
136
+
132
137
  rescue => problem
133
138
  raise RequestError, "Fail to #{method.upcase}: " + problem.to_s
134
139
  end
@@ -1,3 +1,3 @@
1
1
  module Hatenablog
2
- VERSION = "0.5.1"
2
+ VERSION = "0.8.0"
3
3
  end
@@ -0,0 +1,36 @@
1
+ ---
2
+ sources:
3
+ - name: ruby/gem_rbs_collection
4
+ remote: https://github.com/ruby/gem_rbs_collection.git
5
+ revision: main
6
+ repo_dir: gems
7
+ path: ".gem_rbs_collection"
8
+ gems:
9
+ - name: erb
10
+ version: '0'
11
+ source:
12
+ type: stdlib
13
+ - name: net-http
14
+ version: '0'
15
+ source:
16
+ type: stdlib
17
+ - name: set
18
+ version: '0'
19
+ source:
20
+ type: stdlib
21
+ - name: time
22
+ version: '0'
23
+ source:
24
+ type: stdlib
25
+ - name: uri
26
+ version: '0'
27
+ source:
28
+ type: stdlib
29
+ - name: nokogiri
30
+ version: '1.11'
31
+ source:
32
+ type: git
33
+ name: ruby/gem_rbs_collection
34
+ revision: 88e86e0b67262f9ab6244a356e81dd9ca8c55b37
35
+ remote: https://github.com/ruby/gem_rbs_collection.git
36
+ repo_dir: gems
@@ -0,0 +1,36 @@
1
+ # Download sources
2
+ sources:
3
+ - name: ruby/gem_rbs_collection
4
+ remote: https://github.com/ruby/gem_rbs_collection.git
5
+ revision: main
6
+ repo_dir: gems
7
+
8
+ # A directory to install the downloaded RBSs
9
+ path: .gem_rbs_collection
10
+
11
+ gems:
12
+ # stdlibs
13
+ - name: erb
14
+ - name: net-http
15
+ - name: set
16
+ - name: time
17
+ - name: uri
18
+
19
+ # gems
20
+ - name: nokogiri
21
+
22
+ # not used
23
+ - name: activesupport
24
+ ignore: true
25
+ - name: ast
26
+ ignore: true
27
+ - name: listen
28
+ ignore: true
29
+ - name: parallel
30
+ ignore: true
31
+ - name: rainbow
32
+ ignore: true
33
+ - name: rbs
34
+ ignore: true
35
+ - name: steep
36
+ ignore: true
@@ -0,0 +1,249 @@
1
+ module Hatenablog
2
+ VERSION: String
3
+
4
+ class Category
5
+ @document: Nokogiri::XML::Document
6
+ @categories: Array[String]
7
+ @fixed: String
8
+
9
+ def self.load_xml: (String xml) -> Category
10
+
11
+ def categories: () -> Array[String]
12
+ def each: () -> Enumerator[untyped, self]
13
+ | () { (String) -> void } -> Array[String]
14
+ def fixed?: () -> bool
15
+
16
+ private
17
+
18
+ def initialize: (String xml) -> void
19
+ def parse_document: () -> void
20
+ end
21
+
22
+ class Client
23
+ DEFAULT_CONFIG_PATH: String
24
+ COLLECTION_URI: String
25
+ MEMBER_URI: String
26
+ CATEGORY_URI: String
27
+
28
+ @user_id: String
29
+ @blog_id: String
30
+
31
+ attr_writer requester: Requester::Basic | Requester::OAuth
32
+
33
+ def self.create: (?String config_file) -> Client
34
+ | (?String config_file) { (Client) -> void } -> void
35
+
36
+ def initialize: (?Configuration config) ?{ (Configuration) -> void } -> void
37
+ def title: () -> String
38
+ def author_name: () -> String
39
+ def entries: (?Integer page) -> Entries
40
+ def all_entries: () -> Entries
41
+ def next_feed: (?Feed? feed) -> Feed?
42
+ def categories: () -> Array[String]
43
+ def get_entry: (String entry_id) -> Entry
44
+ def post_entry: (?String title, ?String content, ?Array[String] categories, ?String draft) -> Entry
45
+ def update_entry: (String entry_id, ?String title, ?String content, ?Array[String] categories, ?String draft, ?String updated) -> Entry
46
+ def delete_entry: (String entry_id) -> void
47
+ def collection_uri: (?String user_id, ?String blog_id) -> String
48
+ def member_uri: (String entry_id, ?String user_id, ?String blog_id) -> String
49
+ def category_doc_uri: (?String user_id, ?String blog_id) -> String
50
+ def entry_xml: (?String title, ?String content, ?Array[String] categories, ?String draft, ?String updated, ?String author_name) -> String
51
+
52
+ private
53
+
54
+ def get: (String uri) -> Net::HTTPResponse
55
+ def get_collection: (?String uri) -> Net::HTTPResponse
56
+ def get_category_doc: () -> Net::HTTPResponse
57
+ def post: (String entry_xml, ?String uri) -> Net::HTTPResponse
58
+ def put: (String entry_xml, String uri) -> Net::HTTPResponse
59
+ def delete: (String uri) -> Net::HTTPResponse
60
+ end
61
+
62
+ class Entries
63
+ include Enumerable[Entry]
64
+
65
+ @client: Client
66
+ @page: Integer
67
+ @fetch: :partial | :all
68
+
69
+ def initialize: (Client client, ?Integer page, ?(:partial | :all) fetch) -> void
70
+ def each: () -> Enumerator[untyped, self]
71
+ | () { (Entry) -> void } -> Entries
72
+
73
+ private
74
+
75
+ def each_all: () { (Entry) -> void } -> Entries
76
+ def each_partial: () { (Entry) -> void } -> Entries
77
+ end
78
+
79
+ class Configuration < OpenStruct
80
+ OAUTH_KEYS: [:consumer_key, :consumer_secret, :access_token, :access_token_secret, :user_id, :blog_id]
81
+ BASIC_KEYS: [:api_key, :user_id, :blog_id]
82
+
83
+ def self.create: (String) -> Configuration
84
+ def check_valid_or_raise: () -> Configuration
85
+
86
+ # attribute accessors allowed to define dynamically
87
+ def consumer_key: () -> untyped # String?
88
+ def consumer_secret: () -> untyped # String?
89
+ def access_token: () -> untyped # String?
90
+ def access_token_secret: () -> untyped # String?
91
+ def user_id: () -> String
92
+ def blog_id: () -> String
93
+ def api_key: () -> untyped # String?
94
+ def auth_type: () -> untyped # String?
95
+
96
+ private
97
+
98
+ def lacking_keys: () -> (Array[:consumer_key | :consumer_secret | :access_token | :access_token_secret | :user_id | :blog_id | :api_key | :user_id | :blog_id])
99
+ end
100
+
101
+ class ConfigurationError < StandardError
102
+ end
103
+
104
+ module AfterHook
105
+ def after_hook: (Symbol hook, *Symbol methods) -> Array[Symbol]
106
+
107
+ # methods hooked dynamically
108
+ def uri=: (String uri) -> untyped
109
+ def edit_uri=: (String uri) -> untyped
110
+ def author_name=: (String author_name) -> untyped
111
+ def title=: (String title) -> untyped
112
+ def content=: (String content) -> untyped
113
+ def updated=: (String date) -> untyped
114
+ def draft=: (String draft) -> untyped
115
+ def categories=: (Array[String] categories) -> untyped
116
+
117
+ # workaround for using `Module` instance methods in `after_hook`
118
+ def alias_method: (::Symbol | ::String new_name, ::Symbol | ::String old_name) -> ::Symbol
119
+ def define_method: (Symbol | String arg0, ?Proc | Method | UnboundMethod arg1) -> Symbol
120
+ | (Symbol | String arg0) { () -> untyped } -> Symbol
121
+ def instance_methods: (?boolish include_super) -> ::Array[Symbol]
122
+ end
123
+
124
+ class Entry
125
+ extend AfterHook
126
+
127
+ @document: Nokogiri::XML::Document
128
+ @formatted_content: untyped
129
+
130
+ attr_accessor uri: String
131
+ attr_accessor author_name: String
132
+ attr_accessor title: String
133
+ attr_accessor content: String
134
+ attr_accessor draft: String
135
+
136
+ attr_reader edit_uri: String
137
+ attr_reader id: String?
138
+ attr_reader updated : Time?
139
+
140
+ attr_writer categories: Array[String]
141
+
142
+ def self.load_xml: (String xml) -> Entry
143
+ def self.create: (?uri: String, ?edit_uri: String, ?author_name: String, ?title: String, ?content: String, ?draft: String, ?categories: Array[String], ?updated: String) ?{ (Entry) -> void } -> Entry
144
+ def self.build_xml: (String uri, String edit_uri, String author_name, String title, String content, String draft, Array[String]? categories, String? updated) -> String
145
+
146
+ def updated=: (String date) -> Time?
147
+ def edit_uri=: (String uri) -> void
148
+ def draft?: () -> bool
149
+ def categories: () -> Array[String]
150
+ def each_category: () { (String) -> void } -> Array[String]
151
+ def to_xml: () -> String
152
+ def formatted_content: () -> untyped # result of Nokogiri::XML::NodeSet#[]
153
+
154
+ private
155
+
156
+ def initialize: (String xml) -> void
157
+ def parse_document: () -> void
158
+ def parse_categories: () -> Array[untyped]
159
+ def update_xml: () -> void
160
+ def categories_modified?: (Nokogiri::XML::NodeSet old_categories, Array[String] new_categories) -> bool
161
+ end
162
+
163
+ class Feed
164
+ @document: Nokogiri::XML::Document
165
+ @entries: Array[Entry]
166
+
167
+ attr_reader uri: String
168
+ attr_reader next_uri: String
169
+ attr_reader title: String
170
+ attr_reader author_name: String
171
+ attr_reader updated: Time
172
+
173
+ def self.load_xml: (String xml) -> Feed
174
+
175
+ def entries: () -> Array[Entry]
176
+ def each_entry: () { (Entry) -> void } -> Array[Entry]
177
+ def has_next?: () -> bool
178
+
179
+ private
180
+
181
+ def initialize: (String xml) -> void
182
+ def parse_document: () -> void
183
+ def parse_entry: () -> void
184
+ end
185
+
186
+ module Requester
187
+ ATOM_CONTENT_TYPE: String
188
+ DEFAULT_HEADER: Hash[String, String]
189
+
190
+ def self.create: (Configuration config) -> (Basic | OAuth)
191
+
192
+ class RequestError < StandardError
193
+ end
194
+
195
+ class OAuth
196
+ @access_token: ::OAuth::AccessToken
197
+
198
+ def initialize: (::OAuth::AccessToken access_token) -> void
199
+ def get: (String uri) -> Net::HTTPResponse
200
+ def post: (String uri, ?String body, ?Hash[String, String] headers) -> Net::HTTPResponse
201
+ def put: (String uri, ?String body, ?Hash[String, String] headers) -> Net::HTTPResponse
202
+ def delete: (String uri, ?Hash[String, String] headers) -> Net::HTTPResponse
203
+
204
+ private
205
+
206
+ def request: (:get | :post | :put | :delete method, String uri, ?body: String?, ?headers: Hash[String, String]?) -> Net::HTTPResponse
207
+ end
208
+
209
+ class Basic
210
+ METHODS: {get: singleton(Net::HTTP::Get), post: singleton(Net::HTTP::Post), put: singleton(Net::HTTP::Put), delete: singleton(Net::HTTP::Delete)}
211
+
212
+ @user_id: String
213
+ @api_key: String
214
+
215
+ def initialize: (String user_id, String api_key) -> void
216
+ def get: (String uri) -> Net::HTTPResponse
217
+ def post: (String uri, String body, ?Hash[String, String] headers) -> Net::HTTPResponse
218
+ def put: (String uri, String body, ?Hash[String, String] headers) -> Net::HTTPResponse
219
+ def delete: (String uri, ?Hash[String, String] headers) -> Net::HTTPResponse
220
+
221
+ private
222
+
223
+ def request: (String uri, :get | :post | :put | :delete method, ?body: String?, ?headers: Hash[String, String]) -> Net::HTTPResponse
224
+ end
225
+ end
226
+ end
227
+
228
+ # polyfill for ostruct
229
+ class OpenStruct
230
+ def initialize: (?Hash[untyped, untyped]? hash) -> OpenStruct
231
+ def []: (String | Symbol) -> Object
232
+ def to_h: -> Hash[Symbol, Object]
233
+ end
234
+
235
+ # polyfill for oauth
236
+ module OAuth
237
+ class AccessToken
238
+ def initialize: (untyped, untyped, ?untyped) -> void
239
+ end
240
+
241
+ class Consumer
242
+ def initialize: (untyped, untyped, ?untyped) -> void
243
+ end
244
+ end
245
+
246
+ # polyfill for yaml
247
+ module YAML
248
+ def self.load: (String yaml, ?String? filename, ?fallback: bool, ?symbolize_names: bool) -> untyped
249
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hatenablog
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kohei Yamamoto
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-03-13 00:00:00.000000000 Z
11
+ date: 2021-11-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -126,19 +126,21 @@ description: Hatenablog AtomPub API library
126
126
  email:
127
127
  - kymmt90@gmail.com
128
128
  executables:
129
- - get_access_token
129
+ - get_hatena_oauth_access_token
130
130
  extensions: []
131
131
  extra_rdoc_files: []
132
132
  files:
133
+ - ".github/workflows/build.yml"
133
134
  - ".gitignore"
134
- - ".travis.yml"
135
+ - CHANGELOG.md
135
136
  - Gemfile
136
137
  - LICENSE.txt
137
138
  - README.md
138
139
  - Rakefile
140
+ - Steepfile
139
141
  - bin/console
140
142
  - bin/setup
141
- - exe/get_access_token
143
+ - exe/get_hatena_oauth_access_token
142
144
  - hatenablog.gemspec
143
145
  - lib/hatenablog.rb
144
146
  - lib/hatenablog/category.rb
@@ -148,11 +150,14 @@ files:
148
150
  - lib/hatenablog/feed.rb
149
151
  - lib/hatenablog/requester.rb
150
152
  - lib/hatenablog/version.rb
153
+ - rbs_collection.lock.yaml
154
+ - rbs_collection.yaml
155
+ - sig/hatenablog.rbs
151
156
  homepage: https://github.com/kymmt90/hatenablog
152
157
  licenses:
153
158
  - MIT
154
159
  metadata: {}
155
- post_install_message:
160
+ post_install_message:
156
161
  rdoc_options: []
157
162
  require_paths:
158
163
  - lib
@@ -160,16 +165,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
160
165
  requirements:
161
166
  - - ">="
162
167
  - !ruby/object:Gem::Version
163
- version: '2.2'
168
+ version: '2.3'
164
169
  required_rubygems_version: !ruby/object:Gem::Requirement
165
170
  requirements:
166
171
  - - ">="
167
172
  - !ruby/object:Gem::Version
168
173
  version: '0'
169
174
  requirements: []
170
- rubyforge_project:
171
- rubygems_version: 2.6.8
172
- signing_key:
175
+ rubygems_version: 3.2.3
176
+ signing_key:
173
177
  specification_version: 4
174
178
  summary: Hatenablog AtomPub API library
175
179
  test_files: []
data/.travis.yml DELETED
@@ -1,12 +0,0 @@
1
- language: ruby
2
- rvm:
3
- - 2.2.6
4
- - 2.3.3
5
- - 2.4.0
6
- env:
7
- - TZ=Asia/Tokyo
8
- addons:
9
- code_climate:
10
- repo_token: 309cf0784d00d2a6009566d28be111a8a0280cdeb2da280225eedf577b16beb5
11
- after_success:
12
- - bundle exec codeclimate-test-reporter