qiita 0.0.3 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,92 @@
1
+ # ## Qiita::Response
2
+ # A class for response data returned from API.
3
+ #
4
+ module Qiita
5
+ class Response
6
+ def initialize(faraday_response)
7
+ @raw_body = faraday_response.body
8
+ @raw_headers = faraday_response.headers
9
+ @raw_status = faraday_response.status
10
+ end
11
+
12
+ # ### Qiita::Response#body
13
+ # Returns response body returned from API as a `Hash` or an `Array` of `Hash`.
14
+ #
15
+ # ```rb
16
+ # response.body #=> { ... }
17
+ # ```
18
+ #
19
+ def body
20
+ @raw_body
21
+ end
22
+
23
+ # ### Qiita::Response#first_page_url
24
+ # Returns first page URL or nil.
25
+ #
26
+ def first_page_url
27
+ links_table["first"]
28
+ end
29
+
30
+ # ### Qiita::Response#last_page_url
31
+ # Returns last page URL or nil.
32
+ #
33
+ def last_page_url
34
+ links_table["last"]
35
+ end
36
+
37
+ # ### Qiita::Response#next_page_url
38
+ # Returns next page URL or nil.
39
+ #
40
+ def next_page_url
41
+ links_table["next"]
42
+ end
43
+
44
+ # ### Qiita::Response#previous_page_url
45
+ # Returns previous page URL or nil.
46
+ #
47
+ def previous_page_url
48
+ links_table["previous"]
49
+ end
50
+
51
+ # ### Qiita::Response#headers
52
+ # Returns response headers returned from API as a `Hash`.
53
+ #
54
+ # ```rb
55
+ # response.headers #=> { "Content-Type" => "application/json" }
56
+ # ```
57
+ #
58
+ def headers
59
+ @headers ||= @raw_headers.inject({}) do |result, (key, value)|
60
+ result.merge(key.split("-").map(&:capitalize).join("-") => value)
61
+ end
62
+ end
63
+
64
+ # ### Qiita::Response#status
65
+ # Returns response status code returned from API as a `Fixnum`.
66
+ #
67
+ # ```rb
68
+ # response.status #=> 200
69
+ # ```
70
+ #
71
+ def status
72
+ @raw_status
73
+ end
74
+
75
+ def to_s
76
+ @to_s ||= ResponseRenderer.new(self, show_body: true, show_header: true).to_s
77
+ end
78
+
79
+ def status_message
80
+ Rack::Utils::HTTP_STATUS_CODES[status]
81
+ end
82
+
83
+ private
84
+
85
+ def links_table
86
+ @links_table ||= (headers["Link"] || "").split(", ").inject({}) do |table, section|
87
+ url, rel = section.match(/\A<(.+)>; rel="(.+)"\z/)[1..2]
88
+ table.merge(rel => url)
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,79 @@
1
+ module Qiita
2
+ class ResponseRenderer
3
+ def initialize(response, color: nil, show_header: nil, show_body: nil)
4
+ @response = response
5
+ @color = color
6
+ @show_body = show_body
7
+ @show_header = show_header
8
+ end
9
+
10
+ def to_s
11
+ template % {
12
+ status: status,
13
+ headers: headers,
14
+ body: body,
15
+ }
16
+ end
17
+
18
+ private
19
+
20
+ def template
21
+ str = ""
22
+ str << <<-EOS.strip_heredoc if @show_header
23
+ HTTP/1.1 %{status}
24
+ %{headers}
25
+ EOS
26
+ if has_body? && @show_body
27
+ str << "\n" if @show_header
28
+ str << "%{body}"
29
+ end
30
+ str
31
+ end
32
+
33
+ def headers
34
+ @response.headers.sort_by do |key, value|
35
+ key
36
+ end.map do |key, value|
37
+ "%{key}: %{value}" % {
38
+ key: Rainbow(key.split("-").map(&:camelize).join("-")).underline,
39
+ value: Rainbow(value).green,
40
+ }
41
+ end.join("\n")
42
+ end
43
+
44
+ def status
45
+ Rainbow("#{@response.status} #{@response.status_message}").bright
46
+ end
47
+
48
+ def body
49
+ if @color
50
+ Rouge::Formatters::Terminal256.format(
51
+ Rouge::Lexers::Ruby.new.lex(plain_body),
52
+ theme: "github"
53
+ )
54
+ else
55
+ plain_body
56
+ end
57
+ end
58
+
59
+ def plain_body
60
+ if has_body?
61
+ JSON.pretty_generate(@response.body) + "\n"
62
+ else
63
+ ""
64
+ end
65
+ end
66
+
67
+ def Rainbow(str)
68
+ if @color
69
+ super
70
+ else
71
+ Rainbow::NullPresenter.new(str.to_s)
72
+ end
73
+ end
74
+
75
+ def has_body?
76
+ @response.headers['Content-Length'].to_i > 0
77
+ end
78
+ end
79
+ end
data/lib/qiita/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Qiita
2
- VERSION = "0.0.3"
2
+ VERSION = "1.0.0"
3
3
  end
data/qiita.gemspec CHANGED
@@ -1,29 +1,31 @@
1
- # -*- encoding: utf-8 -*-
2
- lib = File.expand_path('../lib', __FILE__)
1
+ lib = File.expand_path("../lib", __FILE__)
3
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'qiita/version'
3
+ require "qiita/version"
5
4
 
6
- Gem::Specification.new do |gem|
7
- gem.name = "qiita"
8
- gem.version = Qiita::VERSION
9
- gem.authors = ["Hiroshige Umino"]
10
- gem.email = ["yaotti@qiita.com"]
11
- gem.description = <<desc
12
- Gets some tag's or user's items at qiita.com.
13
- Creates, updates, deletes and stocks items at Qiita.
14
- desc
15
- gem.summary = "Ruby wrapper for Qiita API v1."
16
- gem.homepage = "http://github.com/yaotti/qiita-rb"
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "qiita"
7
+ spec.version = Qiita::VERSION
8
+ spec.authors = ["Ryo Nakamura"]
9
+ spec.email = ["r7kamura@gmail.com"]
10
+ spec.summary = "A client library for Qiita API v2 written in Ruby."
11
+ spec.homepage = "https://github.com/increments/qiita-rb"
17
12
 
18
- gem.files = `git ls-files`.split($/)
19
- gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
20
- gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
21
- gem.require_paths = ["lib"]
13
+ spec.files = `git ls-files`.split($/)
14
+ spec.executables = spec.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
15
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
16
+ spec.require_paths = ["lib"]
22
17
 
23
- gem.add_dependency 'faraday', '~> 0.8'
24
- gem.add_dependency 'faraday_middleware', '~> 0.8'
25
- gem.add_dependency 'json', '~> 1.7'
26
- gem.add_dependency 'hashie', '~> 1.2'
27
-
28
- gem.add_development_dependency 'rspec'
18
+ spec.add_dependency "activesupport"
19
+ spec.add_dependency "faraday", "~> 0.9"
20
+ spec.add_dependency "faraday_middleware"
21
+ spec.add_dependency "rack"
22
+ spec.add_dependency "rainbow"
23
+ spec.add_dependency "rouge"
24
+ spec.add_dependency "slop"
25
+ spec.add_development_dependency "bundler", "~> 1.6"
26
+ spec.add_development_dependency "json_schema"
27
+ spec.add_development_dependency "pry"
28
+ spec.add_development_dependency "rake"
29
+ spec.add_development_dependency "rspec", "2.14.1"
30
+ spec.add_development_dependency "webmock"
29
31
  end
data/script/generate ADDED
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env ruby
2
+ # # script/generate
3
+ # Generates source code text of Qiita::ResourceBasedMethods from given JSON Schema.
4
+ #
5
+ # ## Usage
6
+ # Run this script with a path to JSON Schema of Qiita API v2, available only for staff.
7
+ #
8
+ # ```sh
9
+ # bundle exec ./script/generate /path/to/json/schema > lib/qiita/resource_based_methods.rb
10
+ # ```
11
+ #
12
+ require "active_support/core_ext/object/try"
13
+ require "active_support/core_ext/string/indent"
14
+ require "active_support/core_ext/string/strip"
15
+ require "json_schema"
16
+ require "ostruct"
17
+ require "yaml"
18
+
19
+ # View object class to generate String representation of each method.
20
+ method_class = Class.new(OpenStruct) do
21
+ def to_s
22
+ <<-EOS.strip_heredoc
23
+ # ### Qiita::Client##{method_name}(#{arguments})
24
+ # #{description}
25
+ #
26
+ def #{method_name}(#{arguments})
27
+ #{request_method}(#{path}, params, headers)
28
+ end
29
+ EOS
30
+ end
31
+
32
+ private
33
+
34
+ def arguments
35
+ [required_argument_names + optional_arguments].join(", ")
36
+ end
37
+
38
+ def optional_arguments
39
+ ["params = nil", "headers = nil"]
40
+ end
41
+
42
+ def path
43
+ ?" + href.gsub(%r<{\(.*/([^/]+)\)}>, '#{\1}') + ?"
44
+ end
45
+ end
46
+
47
+ path_to_json_schema = ARGV[0]
48
+ hash = YAML.load_file(path_to_json_schema)
49
+ schema = JsonSchema.parse!(hash).tap(&:expand_references!)
50
+ links = schema.properties.values.map(&:links).flatten
51
+ methods = links.map do |link|
52
+ method_class.new(
53
+ available_parameters: (link.schema.try(:properties) || {}).map do |name, property|
54
+ {
55
+ description: property.description,
56
+ name: name,
57
+ type: property.type,
58
+ }
59
+ end,
60
+ description: link.description,
61
+ href: link.href,
62
+ method_name: link.title,
63
+ request_method: link.method.downcase,
64
+ required_argument_names: link.href.scan(%r<{\(.*/([^/]+)\)}>).flatten,
65
+ )
66
+ end
67
+
68
+ puts <<-EOS
69
+ module Qiita
70
+ module ResourceBasedMethods
71
+ #{methods.join("\n").indent(4).strip}
72
+ end
73
+ end
74
+ EOS
@@ -0,0 +1,335 @@
1
+ require "spec_helper"
2
+
3
+ describe Qiita::Client do
4
+ def stub_api_request
5
+ stub_request(request_method, requested_url).with(
6
+ body: request_body,
7
+ headers: requested_headers,
8
+ ).to_return(
9
+ body: response_body,
10
+ headers: response_headers,
11
+ status: status_code,
12
+ )
13
+ end
14
+
15
+ let(:status_code) do
16
+ 200
17
+ end
18
+
19
+ let(:response_body) do
20
+ response_hash.to_json
21
+ end
22
+
23
+ let(:response_hash) do
24
+ { "dummy" => "dummy" }
25
+ end
26
+
27
+ let(:response_headers) do
28
+ { "Content-Type" => "application/json" }
29
+ end
30
+
31
+ let(:requested_headers) do
32
+ Qiita::Client::DEFAULT_HEADERS.clone
33
+ end
34
+
35
+ let(:request_body) do
36
+ params.nil? || [:get, :delete].include?(request_method) ? nil : params
37
+ end
38
+
39
+ let(:requested_url) do
40
+ "https://#{requested_host}#{path}"
41
+ end
42
+
43
+ let(:requested_host) do
44
+ Qiita::Client::DEFAULT_HOST
45
+ end
46
+
47
+ let(:host) do
48
+ Qiita::Client::DEFAULT_HOST
49
+ end
50
+
51
+ let(:client) do
52
+ described_class.new(options)
53
+ end
54
+
55
+ let(:options) do
56
+ {}
57
+ end
58
+
59
+ let(:arguments) do
60
+ [path, params, headers]
61
+ end
62
+
63
+ let(:path) do
64
+ "/api/v2/dummy"
65
+ end
66
+
67
+ let(:params) do
68
+ end
69
+
70
+ let(:headers) do
71
+ {}
72
+ end
73
+
74
+ shared_examples_for "returns a Qiita::Response" do
75
+ it "returns a Qiita::Response" do
76
+ should be_a Qiita::Response
77
+ subject.body.should eq response_hash
78
+ subject.headers.should eq response_headers
79
+ subject.status.should eq status_code
80
+ end
81
+ end
82
+
83
+ shared_examples_for "valid condition" do
84
+ context "with valid condition" do
85
+ include_examples "returns a Qiita::Response"
86
+ end
87
+ end
88
+
89
+ shared_examples_for "sends request with JSON-encoded body" do
90
+ context "with params" do
91
+ let(:params) do
92
+ { key1: "value1", key2: "value2" }
93
+ end
94
+
95
+ it "sends request with JSON-encoded body" do
96
+ should be_a Qiita::Response
97
+ end
98
+ end
99
+ end
100
+
101
+ shared_examples_for "sends request with URL query" do
102
+ context "with params" do
103
+ let(:params) do
104
+ { key1: "value1", key2: "value2" }
105
+ end
106
+
107
+ let(:requested_url) do
108
+ super() + "?key1=value1&key2=value2"
109
+ end
110
+
111
+ it "sends request with URL query" do
112
+ should be_a Qiita::Response
113
+ end
114
+ end
115
+ end
116
+
117
+ describe ".new" do
118
+ subject do
119
+ described_class.new(*arguments)
120
+ end
121
+
122
+ let(:arguments) do
123
+ [options]
124
+ end
125
+
126
+ let(:options) do
127
+ {}
128
+ end
129
+
130
+ shared_examples_for "returns a Qiita::Client" do
131
+ it "returns a Qiita::Client" do
132
+ should be_a described_class
133
+ end
134
+ end
135
+
136
+ context "without any arguments" do
137
+ let(:arguments) do
138
+ []
139
+ end
140
+ include_examples "returns a Qiita::Client"
141
+ end
142
+
143
+ context "with :access_token option" do
144
+ let(:options) do
145
+ { access_token: "dummy" }
146
+ end
147
+ include_examples "returns a Qiita::Client"
148
+ end
149
+
150
+ context "with :host option" do
151
+ let(:options) do
152
+ { host: "dummy" }
153
+ end
154
+ include_examples "returns a Qiita::Client"
155
+ end
156
+ end
157
+
158
+ describe "#get" do
159
+ before do
160
+ stub_api_request
161
+ end
162
+
163
+ subject do
164
+ client.get(*arguments)
165
+ end
166
+
167
+ let(:request_method) do
168
+ :get
169
+ end
170
+
171
+ include_examples "valid condition"
172
+ include_examples "sends request with URL query"
173
+
174
+ context "without headers" do
175
+ let(:arguments) do
176
+ [path, params]
177
+ end
178
+ include_examples "returns a Qiita::Response"
179
+ end
180
+
181
+ context "without params nor headers" do
182
+ let(:arguments) do
183
+ [path]
184
+ end
185
+ include_examples "returns a Qiita::Response"
186
+ end
187
+
188
+ context "with a Qiita::Client created with :host option" do
189
+ before do
190
+ options[:host] = host
191
+ end
192
+
193
+ let(:host) do
194
+ "example.com"
195
+ end
196
+
197
+ let(:requested_host) do
198
+ host
199
+ end
200
+
201
+ it "sends request to configured host" do
202
+ should be_a Qiita::Response
203
+ end
204
+ end
205
+
206
+ context "with a Qiita::Client created with :access_token option" do
207
+ before do
208
+ options[:access_token] = access_token
209
+ end
210
+
211
+ let(:access_token) do
212
+ "dummy-access-token"
213
+ end
214
+
215
+ let(:requested_headers) do
216
+ super().merge("Authorization" => "Bearer #{access_token}")
217
+ end
218
+
219
+ it "sends request with configured access token" do
220
+ should be_a Qiita::Response
221
+ end
222
+ end
223
+
224
+ context "with headers" do
225
+ let(:headers) do
226
+ {
227
+ "KEY1" => "value1",
228
+ "KEY2" => "value2",
229
+ }
230
+ end
231
+
232
+ let(:requested_headers) do
233
+ super().merge(headers)
234
+ end
235
+
236
+ it "sends request with given headers" do
237
+ should be_a Qiita::Response
238
+ end
239
+ end
240
+
241
+ context "with params" do
242
+ let(:params) do
243
+ { key1: "value1", key2: "value2" }
244
+ end
245
+
246
+ let(:requested_url) do
247
+ super() + "?key1=value1&key2=value2"
248
+ end
249
+
250
+ it "sends request with URL query, encoded from given params" do
251
+ should be_a Qiita::Response
252
+ end
253
+ end
254
+ end
255
+
256
+ describe "#post" do
257
+ before do
258
+ stub_api_request
259
+ end
260
+
261
+ subject do
262
+ client.post(*arguments)
263
+ end
264
+
265
+ let(:request_method) do
266
+ :post
267
+ end
268
+
269
+ let(:status_code) do
270
+ 201
271
+ end
272
+
273
+ include_examples "valid condition"
274
+ include_examples "sends request with JSON-encoded body"
275
+ end
276
+
277
+ describe "#patch" do
278
+ before do
279
+ stub_api_request
280
+ end
281
+
282
+ subject do
283
+ client.patch(*arguments)
284
+ end
285
+
286
+ let(:request_method) do
287
+ :patch
288
+ end
289
+
290
+ include_examples "valid condition"
291
+ include_examples "sends request with JSON-encoded body"
292
+ end
293
+
294
+ describe "#put" do
295
+ before do
296
+ stub_api_request
297
+ end
298
+
299
+ subject do
300
+ client.put(*arguments)
301
+ end
302
+
303
+ let(:request_method) do
304
+ :put
305
+ end
306
+
307
+ let(:status_code) do
308
+ 204
309
+ end
310
+
311
+ include_examples "valid condition"
312
+ include_examples "sends request with JSON-encoded body"
313
+ end
314
+
315
+ describe "#delete" do
316
+ before do
317
+ stub_api_request
318
+ end
319
+
320
+ subject do
321
+ client.delete(*arguments)
322
+ end
323
+
324
+ let(:request_method) do
325
+ :delete
326
+ end
327
+
328
+ let(:status_code) do
329
+ 204
330
+ end
331
+
332
+ include_examples "valid condition"
333
+ include_examples "sends request with URL query"
334
+ end
335
+ end