qiita 0.0.3 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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