mediawiki_api 0.3.0 → 0.3.1

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
2
  SHA1:
3
- metadata.gz: 20db69d02e685dd6c01f43a617e783310b884784
4
- data.tar.gz: 8ea86db0e30f32006981ba4e04779f0e8eb1b73c
3
+ metadata.gz: b2e13c62db930923fbfd515cb2029d9d43508b9a
4
+ data.tar.gz: dada9ee27c4c09a1a2c7a157828e16079beace62
5
5
  SHA512:
6
- metadata.gz: c5c9196a44d8975fb8081273791487d88188ed4f452c892d656d2de940fc9e1049b61dd64ec14d2a71e7c0c6935109bfb006378eb697bd189f70536d10ed80b9
7
- data.tar.gz: cd1f86124b668e951ec1a608be92ec7c6aee66273eb3c360da85b546ffd460d87427d16a4dcac23c5e0c476c5f77c207cdce673af3053bb4b9a9a3ce03bc21c9
6
+ metadata.gz: 145fc6da63febab8ffc18b4d32a6eda108232052fd927380357a7fdfc375eb482681c1ac339e3299f4fee62c3f05fac2e03d766cd401a891991af74a5eb35111
7
+ data.tar.gz: eba8ace9d4a58f8148c7e5601d6e216475fc0bce8612bd78bea5fab637a63ed46ffcd4b042cb9bad82d0ace755685d025c0b1d89ccf84567c9fc698aeccbf23e
data/.rubocop.yml CHANGED
@@ -1 +1,33 @@
1
- inherit_from: .rubocop_todo.yml
1
+ Metrics/AbcSize:
2
+ Enabled: false
3
+
4
+ Metrics/ClassLength:
5
+ Enabled: false
6
+
7
+ Metrics/MethodLength:
8
+ Enabled: false
9
+
10
+ Metrics/ParameterLists:
11
+ Enabled: false
12
+
13
+ Metrics/LineLength:
14
+ Max: 100
15
+
16
+ Metrics/CyclomaticComplexity:
17
+ Enabled: false
18
+
19
+ Metrics/PerceivedComplexity:
20
+ Enabled: false
21
+
22
+ Metrics/MethodLength:
23
+ Enabled: false
24
+
25
+ Style/Alias:
26
+ Enabled: false
27
+
28
+ Style/DotPosition:
29
+ Enabled: true
30
+ EnforcedStyle: trailing
31
+
32
+ Style/SignalException:
33
+ Enabled: false
data/CREDITS CHANGED
@@ -6,6 +6,7 @@ For further details on licensing, see the LICENSE file.
6
6
 
7
7
  == Developers ==
8
8
  * Amir Aharoni
9
+ * Asaf Bartov
9
10
  * Chris McMahon
10
11
  * Dan Duvall
11
12
  * Jeff Hall
data/Gemfile CHANGED
@@ -1,7 +1,7 @@
1
- #ruby=ruby-2.1.2
2
- #ruby-gemset=mediawiki_api
1
+ # ruby=ruby-2.1.2
2
+ # ruby-gemset=mediawiki_api
3
3
 
4
- source "https://rubygems.org"
4
+ source 'https://rubygems.org'
5
5
 
6
6
  # Specify your gem's dependencies in mediawiki_api.gemspec
7
7
  gemspec
data/README.md CHANGED
@@ -54,6 +54,9 @@ See https://www.mediawiki.org/wiki/Gerrit
54
54
 
55
55
  ## Release notes
56
56
 
57
+ ### 0.3.1 2015-01-06
58
+ - actions now automatically refresh token and re-submit action if first attempt returns 'badtoken'.
59
+
57
60
  ### 0.3.0 2014-10-14
58
61
 
59
62
  - HTTP 400 and 500 responses now result in an HttpError exception.
data/lib/mediawiki_api.rb CHANGED
@@ -1 +1 @@
1
- require "mediawiki_api/client"
1
+ require 'mediawiki_api/client'
@@ -1,17 +1,18 @@
1
- require "faraday"
2
- require "faraday-cookie_jar"
3
- require "json"
1
+ require 'faraday'
2
+ require 'faraday-cookie_jar'
3
+ require 'json'
4
4
 
5
- require "mediawiki_api/exceptions"
6
- require "mediawiki_api/response"
5
+ require 'mediawiki_api/exceptions'
6
+ require 'mediawiki_api/response'
7
7
 
8
8
  module MediawikiApi
9
+ # high level client for MediaWiki
9
10
  class Client
10
- FORMAT = "json"
11
+ FORMAT = 'json'
11
12
 
12
13
  attr_accessor :logged_in
13
14
 
14
- alias logged_in? logged_in
15
+ alias_method :logged_in?, :logged_in
15
16
 
16
17
  def initialize(url, log = false)
17
18
  @conn = Faraday.new(url: url) do |faraday|
@@ -26,24 +27,14 @@ module MediawikiApi
26
27
  end
27
28
 
28
29
  def action(name, params = {})
29
- name = name.to_s
30
-
31
- method = params.delete(:http_method) || :post
32
- token_type = params.delete(:token_type)
33
- envelope = (params.delete(:envelope) || [name]).map(&:to_s)
34
-
35
- params[:token] = get_token(token_type || name) unless token_type == false
36
- params = compile_parameters(params)
37
-
38
- response = @conn.send(method, "", params.merge(action: name, format: FORMAT))
39
-
40
- raise HttpError, response.status if response.status >= 400
41
-
42
- if response.headers.include?("mediawiki-api-error")
43
- raise ApiError, Response.new(response, ["error"])
30
+ raw_action(name, params)
31
+ rescue ApiError => e
32
+ if e.code == 'badtoken'
33
+ @tokens.clear # ensure fresh token on re-try
34
+ raw_action(name, params) # no rescue this time; only re-try once.
35
+ else
36
+ raise # otherwise, propagate the exception
44
37
  end
45
-
46
- Response.new(response, envelope)
47
38
  end
48
39
 
49
40
  def create_account(username, password, token = nil)
@@ -52,14 +43,14 @@ module MediawikiApi
52
43
 
53
44
  data = action(:createaccount, params).data
54
45
 
55
- case data["result"]
56
- when "Success"
46
+ case data['result']
47
+ when 'Success'
57
48
  @logged_in = true
58
49
  @tokens.clear
59
- when "NeedToken"
60
- data = create_account(username, password, data["token"])
50
+ when 'NeedToken'
51
+ data = create_account(username, password, data['token'])
61
52
  else
62
- raise CreateAccountError, data["result"]
53
+ raise CreateAccountError, data['result']
63
54
  end
64
55
 
65
56
  data
@@ -75,12 +66,12 @@ module MediawikiApi
75
66
 
76
67
  def edit(params = {})
77
68
  response = action(:edit, params)
78
- raise EditError, response if response.data["result"] == "Failure"
69
+ raise EditError, response if response.data['result'] == 'Failure'
79
70
  response
80
71
  end
81
72
 
82
73
  def get_wikitext(title)
83
- @conn.get "/w/index.php", { action: "raw", title: title }
74
+ @conn.get '/w/index.php', action: 'raw', title: title
84
75
  end
85
76
 
86
77
  def list(type, params = {})
@@ -93,14 +84,14 @@ module MediawikiApi
93
84
 
94
85
  data = action(:login, params).data
95
86
 
96
- case data["result"]
97
- when "Success"
87
+ case data['result']
88
+ when 'Success'
98
89
  @logged_in = true
99
90
  @tokens.clear
100
- when "NeedToken"
101
- data = log_in(username, password, data["token"])
91
+ when 'NeedToken'
92
+ data = log_in(username, password, data['token'])
102
93
  else
103
- raise LoginError, data["result"]
94
+ raise LoginError, data['result']
104
95
  end
105
96
 
106
97
  data
@@ -114,7 +105,7 @@ module MediawikiApi
114
105
  subquery(:prop, type, params)
115
106
  end
116
107
 
117
- def protect_page(title, reason, protections = "edit=sysop|move=sysop")
108
+ def protect_page(title, reason, protections = 'edit=sysop|move=sysop')
118
109
  action(:protect, title: title, reason: reason, protections: protections)
119
110
  end
120
111
 
@@ -127,8 +118,10 @@ module MediawikiApi
127
118
  end
128
119
 
129
120
  def upload_image(filename, path, comment, ignorewarnings)
130
- file = Faraday::UploadIO.new(path, "image/png")
131
- action(:upload, token_type: "edit", filename: filename, file: file, comment: comment, ignorewarnings: ignorewarnings)
121
+ file = Faraday::UploadIO.new(path, 'image/png')
122
+ action(:upload,
123
+ token_type: 'edit', filename: filename, file: file, comment: comment,
124
+ ignorewarnings: ignorewarnings)
132
125
  end
133
126
 
134
127
  def watch_page(title)
@@ -143,7 +136,7 @@ module MediawikiApi
143
136
  when false
144
137
  # omit it entirely
145
138
  when Array
146
- params[name] = value.join("|")
139
+ params[name] = value.join('|')
147
140
  else
148
141
  params[name] = value
149
142
  end
@@ -153,9 +146,10 @@ module MediawikiApi
153
146
  def get_token(type)
154
147
  unless @tokens.include?(type)
155
148
  response = action(:tokens, type: type, http_method: :get, token_type: false)
149
+ parameter_warning = /Unrecognized value for parameter 'type'/
156
150
 
157
- if response.warnings? && response.warnings.grep(/Unrecognized value for parameter 'type'/).any?
158
- raise TokenError, response.warnings.join(", ")
151
+ if response.warnings? && response.warnings.grep(parameter_warning).any?
152
+ raise TokenError, response.warnings.join(', ')
159
153
  end
160
154
 
161
155
  @tokens[type] = response.data["#{type}token"]
@@ -164,8 +158,34 @@ module MediawikiApi
164
158
  @tokens[type]
165
159
  end
166
160
 
161
+ def send_request(method, params, envelope)
162
+ response = @conn.send(method, '', params)
163
+
164
+ raise HttpError, response.status if response.status >= 400
165
+
166
+ if response.headers.include?('mediawiki-api-error')
167
+ raise ApiError, Response.new(response, ['error'])
168
+ end
169
+
170
+ Response.new(response, envelope)
171
+ end
172
+
167
173
  def subquery(type, subtype, params = {})
168
- query(params.merge(type.to_sym => subtype, :envelope => ["query", subtype]))
174
+ query(params.merge(type.to_sym => subtype, :envelope => ['query', subtype]))
175
+ end
176
+
177
+ def raw_action(name, params = {})
178
+ name = name.to_s
179
+ params = params.clone
180
+
181
+ method = params.delete(:http_method) || :post
182
+ token_type = params.delete(:token_type)
183
+ envelope = (params.delete(:envelope) || [name]).map(&:to_s)
184
+
185
+ params[:token] = get_token(token_type || name) unless token_type == false
186
+ params = compile_parameters(params)
187
+
188
+ send_request(method, params.merge(action: name, format: FORMAT), envelope)
169
189
  end
170
190
  end
171
191
  end
@@ -1,4 +1,5 @@
1
1
  module MediawikiApi
2
+ # generic MediaWiki api errors
2
3
  class ApiError < StandardError
3
4
  attr_reader :response
4
5
 
@@ -7,11 +8,11 @@ module MediawikiApi
7
8
  end
8
9
 
9
10
  def code
10
- data["code"]
11
+ data['code']
11
12
  end
12
13
 
13
14
  def info
14
- data["info"]
15
+ data['info']
15
16
  end
16
17
 
17
18
  def to_s
@@ -28,6 +29,7 @@ module MediawikiApi
28
29
  class CreateAccountError < StandardError
29
30
  end
30
31
 
32
+ # for errors from HTTP requests
31
33
  class HttpError < StandardError
32
34
  attr_reader :status
33
35
 
@@ -40,9 +42,10 @@ module MediawikiApi
40
42
  end
41
43
  end
42
44
 
45
+ # for edit failures
43
46
  class EditError < ApiError
44
47
  def to_s
45
- "check the response data for details"
48
+ 'check the response data for details'
46
49
  end
47
50
  end
48
51
 
@@ -1,5 +1,5 @@
1
- require "forwardable"
2
- require "json"
1
+ require 'forwardable'
2
+ require 'json'
3
3
 
4
4
  module MediawikiApi
5
5
  # Provides access to a parsed MediaWiki API responses.
@@ -51,16 +51,20 @@ module MediawikiApi
51
51
  end
52
52
  end
53
53
 
54
+ # Set of error messages from the response.
55
+ #
56
+ # @return [Array]
57
+ #
58
+ def errors
59
+ flatten_resp('errors')
60
+ end
61
+
54
62
  # Set of warning messages from the response.
55
63
  #
56
64
  # @return [Array]
57
65
  #
58
66
  def warnings
59
- if response_object["warnings"]
60
- response_object["warnings"].values.map(&:values).flatten
61
- else
62
- []
63
- end
67
+ flatten_resp('warnings')
64
68
  end
65
69
 
66
70
  # Whether the response contains warnings.
@@ -73,6 +77,14 @@ module MediawikiApi
73
77
 
74
78
  private
75
79
 
80
+ def flatten_resp(str)
81
+ if response_object[str]
82
+ response_object[str].values.map(&:values).flatten
83
+ else
84
+ []
85
+ end
86
+ end
87
+
76
88
  def open_envelope(obj, env = @envelope)
77
89
  if !obj.is_a?(Hash) || env.nil? || env.empty? || !obj.include?(env.first)
78
90
  obj
@@ -1,3 +1,4 @@
1
+ # MediaWiki Ruby API
1
2
  module MediawikiApi
2
- VERSION = "0.3.0"
3
+ VERSION = '0.3.1'
3
4
  end
@@ -1,29 +1,37 @@
1
1
  # coding: utf-8
2
- lib = File.expand_path("../lib", __FILE__)
2
+ lib = File.expand_path('../lib', __FILE__)
3
3
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require "mediawiki_api/version"
4
+ require 'mediawiki_api/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
- spec.name = "mediawiki_api"
7
+ spec.name = 'mediawiki_api'
8
8
  spec.version = MediawikiApi::VERSION
9
- spec.authors = ["Amir Aharoni", "Chris McMahon", "Dan Duvall", "Jeff Hall", "Juliusz Gonera", "Zeljko Filipin"]
10
- spec.email = ["amir.aharoni@mail.huji.ac.il", "cmcmahon@wikimedia.org", "dduvall@wikimedia.org", "jhall@wikimedia.org", "jgonera@wikimedia.org", "zeljko.filipin@gmail.com"]
11
- spec.summary = %q{A library for interacting with MediaWiki API from Ruby.}
12
- spec.description = %q{Uses adapter-agnostic Faraday gem to talk to MediaWiki API.}
13
- spec.homepage = "https://github.com/wikimedia/mediawiki-ruby-api"
14
- spec.license = "GPL-2"
9
+ spec.authors = [
10
+ 'Amir Aharoni', 'Asaf Bartov', 'Chris McMahon', 'Dan Duvall', 'Jeff Hall', 'Juliusz Gonera',
11
+ 'Zeljko Filipin'
12
+ ]
13
+ spec.email = [
14
+ 'amir.aharoni@mail.huji.ac.il', 'asaf.bartov@gmail.com', 'cmcmahon@wikimedia.org',
15
+ 'dduvall@wikimedia.org', 'jhall@wikimedia.org', 'jgonera@wikimedia.org',
16
+ 'zeljko.filipin@gmail.com'
17
+ ]
18
+ spec.summary = 'A library for interacting with MediaWiki API from Ruby.'
19
+ spec.description = 'Uses adapter-agnostic Faraday gem to talk to MediaWiki API.'
20
+ spec.homepage = 'https://github.com/wikimedia/mediawiki-ruby-api'
21
+ spec.license = 'GPL-2'
15
22
 
16
- spec.files = `git ls-files`.split($/)
17
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
- spec.require_paths = ["lib"]
23
+ spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
24
+ spec.test_files = spec.files.grep(/^(test|spec|features)/)
25
+ spec.require_paths = ['lib']
19
26
 
20
- spec.add_runtime_dependency "faraday", "~> 0.9", ">= 0.9.0"
21
- spec.add_runtime_dependency "faraday-cookie_jar", "~> 0.0", ">= 0.0.6"
27
+ spec.add_runtime_dependency 'faraday', '~> 0.9', '>= 0.9.0'
28
+ spec.add_runtime_dependency 'faraday-cookie_jar', '~> 0.0', '>= 0.0.6'
22
29
 
23
- spec.add_development_dependency "bundler", "~> 1.3"
24
- spec.add_development_dependency "rake", "~> 0"
25
- spec.add_development_dependency "rspec", "~> 3.0", ">= 3.0.0"
26
- spec.add_development_dependency "webmock", "~> 1.17", ">= 1.17.2"
27
- spec.add_development_dependency "redcarpet"
28
- spec.add_development_dependency "yard"
30
+ spec.add_development_dependency 'bundler', '~> 1.3'
31
+ spec.add_development_dependency 'rake', '~> 0'
32
+ spec.add_development_dependency 'rspec', '~> 3.0', '>= 3.0.0'
33
+ spec.add_development_dependency 'rubocop', '~> 0.26.1'
34
+ spec.add_development_dependency 'webmock', '~> 1.17', '>= 1.17.2'
35
+ spec.add_development_dependency 'redcarpet'
36
+ spec.add_development_dependency 'yard'
29
37
  end
data/spec/client_spec.rb CHANGED
@@ -1,6 +1,6 @@
1
- require "spec_helper"
2
- require "webmock/rspec"
3
- require "support/request_helpers"
1
+ require 'spec_helper'
2
+ require 'webmock/rspec'
3
+ require 'support/request_helpers'
4
4
 
5
5
  describe MediawikiApi::Client do
6
6
  include MediawikiApi::RequestHelpers
@@ -9,19 +9,22 @@ describe MediawikiApi::Client do
9
9
 
10
10
  subject { client }
11
11
 
12
- body_base = { cookieprefix: "prefix", sessionid: "123" }
12
+ body_base = { cookieprefix: 'prefix', sessionid: '123' }
13
13
 
14
- describe "#action" do
14
+ describe '#action' do
15
15
  subject { client.action(action, params) }
16
16
 
17
- let(:action) { "something" }
17
+ let(:action) { 'something' }
18
18
  let(:token_type) { action }
19
19
  let(:params) { {} }
20
20
 
21
- let(:response) { { status: response_status, headers: response_headers, body: response_body.to_json } }
21
+ let(:response) do
22
+ { status: response_status, headers: response_headers, body: response_body.to_json }
23
+ end
24
+
22
25
  let(:response_status) { 200 }
23
26
  let(:response_headers) { nil }
24
- let(:response_body) { { "something" => {} } }
27
+ let(:response_body) { { 'something' => {} } }
25
28
 
26
29
  let(:token_warning) { nil }
27
30
 
@@ -32,13 +35,13 @@ describe MediawikiApi::Client do
32
35
 
33
36
  it { is_expected.to be_a(MediawikiApi::Response) }
34
37
 
35
- it "makes requests for both the right token and API action" do
38
+ it 'makes requests for both the right token and API action' do
36
39
  subject
37
40
  expect(@token_request).to have_been_made
38
41
  expect(@request).to have_been_made
39
42
  end
40
43
 
41
- context "without a required token" do
44
+ context 'without a required token' do
42
45
  let(:params) { { token_type: false } }
43
46
 
44
47
  before do
@@ -46,33 +49,33 @@ describe MediawikiApi::Client do
46
49
  @request_without_token = stub_api_request(:post, action: action).to_return(response)
47
50
  end
48
51
 
49
- it "does not request a token" do
52
+ it 'does not request a token' do
50
53
  subject
51
54
  expect(@token_request).to_not have_been_made
52
55
  end
53
56
 
54
- it "makes the action request without a token" do
57
+ it 'makes the action request without a token' do
55
58
  subject
56
59
  expect(@request_without_token).to have_been_made
57
60
  expect(@request_with_token).to_not have_been_made
58
61
  end
59
62
  end
60
63
 
61
- context "given parameters" do
62
- let(:params) { { foo: "value" } }
64
+ context 'given parameters' do
65
+ let(:params) { { foo: 'value' } }
63
66
 
64
67
  before do
65
- @request_with_parameters = stub_action_request(action, foo: "value").to_return(response)
68
+ @request_with_parameters = stub_action_request(action, foo: 'value').to_return(response)
66
69
  end
67
70
 
68
- it "includes them" do
71
+ it 'includes them' do
69
72
  subject
70
73
  expect(@request_with_parameters).to have_been_made
71
74
  end
72
75
  end
73
76
 
74
- context "parameter compilation" do
75
- context "negated parameters" do
77
+ context 'parameter compilation' do
78
+ context 'negated parameters' do
76
79
  let(:params) { { foo: false } }
77
80
 
78
81
  before do
@@ -80,136 +83,160 @@ describe MediawikiApi::Client do
80
83
  @request_without_parameter = stub_action_request(action).to_return(response)
81
84
  end
82
85
 
83
- it "omits the parameter" do
86
+ it 'omits the parameter' do
84
87
  subject
85
88
  expect(@request_with_parameter).to_not have_been_made
86
89
  expect(@request_without_parameter).to have_been_made
87
90
  end
88
91
  end
89
92
 
90
- context "array parameters" do
91
- let(:params) { { foo: ["one", "two"] } }
93
+ context 'array parameters' do
94
+ let(:params) { { foo: %w(one two) } }
92
95
 
93
96
  before do
94
- @request = stub_action_request(action, foo: "one|two").to_return(response)
97
+ @request = stub_action_request(action, foo: 'one|two').to_return(response)
95
98
  end
96
99
 
97
- it "pipe delimits values" do
100
+ it 'pipe delimits values' do
98
101
  subject
99
102
  expect(@request).to have_been_made
100
103
  end
101
104
  end
102
105
  end
103
106
 
104
- context "when the response status is in the 400 range" do
107
+ context 'when the response status is in the 400 range' do
105
108
  let(:response_status) { 403 }
106
109
 
107
- it "raises an HttpError" do
108
- expect { subject }.to raise_error(MediawikiApi::HttpError, "unexpected HTTP response (403)")
110
+ it 'raises an HttpError' do
111
+ expect { subject }.to raise_error(MediawikiApi::HttpError,
112
+ 'unexpected HTTP response (403)')
109
113
  end
110
114
  end
111
115
 
112
- context "when the response status is in the 500 range" do
116
+ context 'when the response status is in the 500 range' do
113
117
  let(:response_status) { 502 }
114
118
 
115
- it "raises an HttpError" do
116
- expect { subject }.to raise_error(MediawikiApi::HttpError, "unexpected HTTP response (502)")
119
+ it 'raises an HttpError' do
120
+ expect { subject }.to raise_error(MediawikiApi::HttpError,
121
+ 'unexpected HTTP response (502)')
117
122
  end
118
123
  end
119
124
 
120
- context "when the response is an error" do
121
- let(:response_headers) { { "MediaWiki-API-Error" => "code" } }
122
- let(:response_body) { { error: { info: "detailed message", code: "code" } } }
125
+ context 'when the response is an error' do
126
+ let(:response_headers) { { 'MediaWiki-API-Error' => 'code' } }
127
+ let(:response_body) { { error: { info: 'detailed message', code: 'code' } } }
123
128
 
124
- it "raises an ApiError" do
125
- expect { subject }.to raise_error(MediawikiApi::ApiError, "detailed message (code)")
129
+ it 'raises an ApiError' do
130
+ expect { subject }.to raise_error(MediawikiApi::ApiError, 'detailed message (code)')
126
131
  end
127
132
  end
128
133
 
129
- context "given a bad token type" do
134
+ context 'given a bad token type' do
130
135
  let(:params) { { token_type: token_type } }
131
- let(:token_type) { "badtoken" }
136
+ let(:token_type) { 'badtoken' }
132
137
  let(:token_warning) { "Unrecognized value for parameter 'type': badtoken" }
133
138
 
134
- it "raises a TokenError" do
139
+ it 'raises a TokenError' do
135
140
  expect { subject }.to raise_error(MediawikiApi::TokenError, token_warning)
136
141
  end
137
142
  end
138
143
 
139
- context "when the token response includes only other types of warnings (see bug 70066)" do
140
- let(:token_warning) { "action=tokens has been deprecated. Please use action=query&meta=tokens instead." }
144
+ context 'when the token response includes only other types of warnings (see bug 70066)' do
145
+ let(:token_warning) do
146
+ 'action=tokens has been deprecated. Please use action=query&meta=tokens instead.'
147
+ end
148
+
149
+ it 'raises no exception' do
150
+ expect { subject }.to_not raise_error
151
+ end
152
+ end
153
+
154
+ context 'when the token is invalid' do
155
+ let(:response_headers) { { 'MediaWiki-API-Error' => 'badtoken' } }
156
+ let(:response_body) { { error: { code: 'badtoken', info: 'Invalid token' } } }
141
157
 
142
- it "raises no exception" do
158
+ before do
159
+ # Stub a second request without the error
160
+ @request.then.to_return(status: 200)
161
+ end
162
+
163
+ it 'rescues the initial exception' do
143
164
  expect { subject }.to_not raise_error
144
165
  end
166
+
167
+ it 'automatically retries the request' do
168
+ subject
169
+ expect(@token_request).to have_been_made.twice
170
+ expect(@request).to have_been_made.twice
171
+ end
145
172
  end
146
173
  end
147
174
 
148
- describe "#log_in" do
175
+ describe '#log_in' do
149
176
 
150
- it "logs in when API returns Success" do
177
+ it 'logs in when API returns Success' do
151
178
  stub_request(:post, api_url).
152
- with(body: { format: "json", action: "login", lgname: "Test", lgpassword: "qwe123" }).
153
- to_return(body: { login: body_base.merge({ result: "Success" }) }.to_json )
179
+ with(body: { format: 'json', action: 'login', lgname: 'Test', lgpassword: 'qwe123' }).
180
+ to_return(body: { login: body_base.merge(result: 'Success') }.to_json)
154
181
 
155
- subject.log_in "Test", "qwe123"
182
+ subject.log_in 'Test', 'qwe123'
156
183
  expect(subject.logged_in).to be true
157
184
  end
158
185
 
159
- context "when API returns NeedToken" do
186
+ context 'when API returns NeedToken' do
160
187
  before do
161
- headers = { "Set-Cookie" => "prefixSession=789; path=/; domain=localhost; HttpOnly" }
162
-
163
188
  stub_request(:post, api_url).
164
- with(body: { format: "json", action: "login", lgname: "Test", lgpassword: "qwe123" }).
189
+ with(body: { format: 'json', action: 'login', lgname: 'Test', lgpassword: 'qwe123' }).
165
190
  to_return(
166
- body: { login: body_base.merge({ result: "NeedToken", token: "456" }) }.to_json,
167
- headers: { "Set-Cookie" => "prefixSession=789; path=/; domain=localhost; HttpOnly" }
191
+ body: { login: body_base.merge(result: 'NeedToken', token: '456') }.to_json,
192
+ headers: { 'Set-Cookie' => 'prefixSession=789; path=/; domain=localhost; HttpOnly' }
168
193
  )
169
194
 
170
195
  @success_req = stub_request(:post, api_url).
171
- with(body: { format: "json", action: "login", lgname: "Test", lgpassword: "qwe123", lgtoken: "456" }).
172
- with(headers: { "Cookie" => "prefixSession=789" }).
173
- to_return(body: { login: body_base.merge({ result: "Success" }) }.to_json )
196
+ with(body: { format: 'json', action: 'login',
197
+ lgname: 'Test', lgpassword: 'qwe123', lgtoken: '456' }).
198
+ with(headers: { 'Cookie' => 'prefixSession=789' }).
199
+ to_return(body: { login: body_base.merge(result: 'Success') }.to_json)
174
200
  end
175
201
 
176
- it "logs in" do
177
- response = subject.log_in("Test", "qwe123")
202
+ it 'logs in' do
203
+ response = subject.log_in('Test', 'qwe123')
178
204
 
179
- expect(response).to include("result" => "Success")
205
+ expect(response).to include('result' => 'Success')
180
206
  expect(subject.logged_in).to be true
181
207
  end
182
208
 
183
- it "sends second request with token and cookies" do
184
- response = subject.log_in("Test", "qwe123")
209
+ it 'sends second request with token and cookies' do
210
+ subject.log_in('Test', 'qwe123')
185
211
 
186
212
  expect(@success_req).to have_been_requested
187
213
  end
188
214
  end
189
215
 
190
- context "when API returns neither Success nor NeedToken" do
216
+ context 'when API returns neither Success nor NeedToken' do
191
217
  before do
192
218
  stub_request(:post, api_url).
193
- with(body: { format: "json", action: "login", lgname: "Test", lgpassword: "qwe123" }).
194
- to_return(body: { login: body_base.merge({ result: "EmptyPass" }) }.to_json )
219
+ with(body: { format: 'json', action: 'login', lgname: 'Test', lgpassword: 'qwe123' }).
220
+ to_return(body: { login: body_base.merge(result: 'EmptyPass') }.to_json)
195
221
  end
196
222
 
197
- it "does not log in" do
198
- expect { subject.log_in "Test", "qwe123" }.to raise_error
223
+ it 'does not log in' do
224
+ expect { subject.log_in 'Test', 'qwe123' }.to raise_error
199
225
  expect(subject.logged_in).to be false
200
226
  end
201
227
 
202
- it "raises error with proper message" do
203
- expect { subject.log_in "Test", "qwe123" }.to raise_error MediawikiApi::LoginError, "EmptyPass"
228
+ it 'raises error with proper message' do
229
+ expect { subject.log_in 'Test', 'qwe123' }.to raise_error(MediawikiApi::LoginError,
230
+ 'EmptyPass')
204
231
  end
205
232
  end
206
233
  end
207
234
 
208
- describe "#create_page" do
235
+ describe '#create_page' do
209
236
  subject { client.create_page(title, text) }
210
237
 
211
- let(:title) { "Test" }
212
- let(:text) { "test123" }
238
+ let(:title) { 'Test' }
239
+ let(:text) { 'test123' }
213
240
  let(:response) { {} }
214
241
 
215
242
  before do
@@ -218,30 +245,31 @@ describe MediawikiApi::Client do
218
245
  to_return(body: response.to_json)
219
246
  end
220
247
 
221
- it "makes the right request" do
248
+ it 'makes the right request' do
222
249
  subject
223
250
  expect(@edit_request).to have_been_requested
224
251
  end
225
252
  end
226
253
 
227
- describe "#delete_page" do
254
+ describe '#delete_page' do
228
255
  before do
229
256
  stub_request(:get, api_url).
230
- with(query: { format: "json", action: "tokens", type: "delete" }).
231
- to_return(body: { tokens: { deletetoken: "t123" } }.to_json )
257
+ with(query: { format: 'json', action: 'tokens', type: 'delete' }).
258
+ to_return(body: { tokens: { deletetoken: 't123' } }.to_json)
232
259
  @delete_req = stub_request(:post, api_url).
233
- with(body: { format: "json", action: "delete", title: "Test", reason: "deleting", token: "t123" })
260
+ with(body: { format: 'json', action: 'delete',
261
+ title: 'Test', reason: 'deleting', token: 't123' })
234
262
  end
235
263
 
236
- it "deletes a page using a delete token" do
237
- subject.delete_page("Test", "deleting")
264
+ it 'deletes a page using a delete token' do
265
+ subject.delete_page('Test', 'deleting')
238
266
  expect(@delete_req).to have_been_requested
239
267
  end
240
268
 
241
269
  # evaluate results
242
270
  end
243
271
 
244
- describe "#edit" do
272
+ describe '#edit' do
245
273
  subject { client.edit(params) }
246
274
 
247
275
  let(:params) { {} }
@@ -252,108 +280,113 @@ describe MediawikiApi::Client do
252
280
  @edit_request = stub_action_request(:edit).to_return(body: response.to_json)
253
281
  end
254
282
 
255
- it "makes the request" do
283
+ it 'makes the request' do
256
284
  subject
257
285
  expect(@edit_request).to have_been_requested
258
286
  end
259
287
 
260
- context "upon an edit failure" do
261
- let(:response) { { edit: { result: "Failure" } } }
288
+ context 'upon an edit failure' do
289
+ let(:response) { { edit: { result: 'Failure' } } }
262
290
 
263
- it "raises an EditError" do
291
+ it 'raises an EditError' do
264
292
  expect { subject }.to raise_error(MediawikiApi::EditError)
265
293
  end
266
294
  end
267
295
  end
268
296
 
269
- describe "#get_wikitext" do
297
+ describe '#get_wikitext' do
270
298
  before do
271
- @get_req = stub_request(:get, index_url).with(query: { action: "raw", title: "Test" })
299
+ @get_req = stub_request(:get, index_url).with(query: { action: 'raw', title: 'Test' })
272
300
  end
273
301
 
274
- it "fetches a page" do
275
- subject.get_wikitext("Test")
302
+ it 'fetches a page' do
303
+ subject.get_wikitext('Test')
276
304
  expect(@get_req).to have_been_requested
277
305
  end
278
306
  end
279
307
 
280
- describe "#create_account" do
281
- it "creates an account when API returns Success" do
308
+ describe '#create_account' do
309
+ it 'creates an account when API returns Success' do
282
310
  stub_request(:post, api_url).
283
- with(body: { format: "json", action: "createaccount", name: "Test", password: "qwe123" }).
284
- to_return(body: { createaccount: body_base.merge({ result: "Success" }) }.to_json )
311
+ with(body: { format: 'json', action: 'createaccount', name: 'Test', password: 'qwe123' }).
312
+ to_return(body: { createaccount: body_base.merge(result: 'Success') }.to_json)
285
313
 
286
- expect(subject.create_account("Test", "qwe123")).to include("result" => "Success")
314
+ expect(subject.create_account('Test', 'qwe123')).to include('result' => 'Success')
287
315
  end
288
316
 
289
- context "when API returns NeedToken" do
317
+ context 'when API returns NeedToken' do
290
318
  before do
291
- headers = { "Set-Cookie" => "prefixSession=789; path=/; domain=localhost; HttpOnly" }
292
-
293
319
  stub_request(:post, api_url).
294
- with(body: { format: "json", action: "createaccount", name: "Test", password: "qwe123" }).
320
+ with(body: { format: 'json', action: 'createaccount',
321
+ name: 'Test', password: 'qwe123' }).
295
322
  to_return(
296
- body: { createaccount: body_base.merge({ result: "NeedToken", token: "456" }) }.to_json,
297
- headers: { "Set-Cookie" => "prefixSession=789; path=/; domain=localhost; HttpOnly" }
323
+ body: { createaccount: body_base.merge(result: 'NeedToken', token: '456') }.to_json,
324
+ headers: { 'Set-Cookie' => 'prefixSession=789; path=/; domain=localhost; HttpOnly' }
298
325
  )
299
326
 
300
327
  @success_req = stub_request(:post, api_url).
301
- with(body: { format: "json", action: "createaccount", name: "Test", password: "qwe123", token: "456" }).
302
- with(headers: { "Cookie" => "prefixSession=789" }).
303
- to_return(body: { createaccount: body_base.merge({ result: "Success" }) }.to_json )
328
+ with(body: { format: 'json', action: 'createaccount',
329
+ name: 'Test', password: 'qwe123', token: '456' }).
330
+ with(headers: { 'Cookie' => 'prefixSession=789' }).
331
+ to_return(body: { createaccount: body_base.merge(result: 'Success') }.to_json)
304
332
  end
305
333
 
306
- it "creates an account" do
307
- expect(subject.create_account("Test", "qwe123")).to include("result" => "Success")
334
+ it 'creates an account' do
335
+ expect(subject.create_account('Test', 'qwe123')).to include('result' => 'Success')
308
336
  end
309
337
 
310
- it "sends second request with token and cookies" do
311
- subject.create_account "Test", "qwe123"
338
+ it 'sends second request with token and cookies' do
339
+ subject.create_account 'Test', 'qwe123'
312
340
  expect(@success_req).to have_been_requested
313
341
  end
314
342
  end
315
343
 
316
344
  # docs don't specify other results, but who knows
317
345
  # http://www.mediawiki.org/wiki/API:Account_creation
318
- context "when API returns neither Success nor NeedToken" do
346
+ context 'when API returns neither Success nor NeedToken' do
319
347
  before do
320
348
  stub_request(:post, api_url).
321
- with(body: { format: "json", action: "createaccount", name: "Test", password: "qwe123" }).
322
- to_return(body: { createaccount: body_base.merge({ result: "WhoKnows" }) }.to_json )
349
+ with(body: { format: 'json', action: 'createaccount',
350
+ name: 'Test', password: 'qwe123' }).
351
+ to_return(body: { createaccount: body_base.merge(result: 'WhoKnows') }.to_json)
323
352
  end
324
353
 
325
- it "raises error with proper message" do
326
- expect { subject.create_account "Test", "qwe123" }.to raise_error MediawikiApi::CreateAccountError, "WhoKnows"
354
+ it 'raises error with proper message' do
355
+ expect { subject.create_account 'Test', 'qwe123' }.to raise_error(
356
+ MediawikiApi::CreateAccountError,
357
+ 'WhoKnows'
358
+ )
327
359
  end
328
360
  end
329
361
  end
330
362
 
331
- describe "#watch_page" do
363
+ describe '#watch_page' do
332
364
  before do
333
365
  stub_request(:get, api_url).
334
- with(query: { format: "json", action: "tokens", type: "watch" }).
335
- to_return(body: { tokens: { watchtoken: "t123" } }.to_json )
366
+ with(query: { format: 'json', action: 'tokens', type: 'watch' }).
367
+ to_return(body: { tokens: { watchtoken: 't123' } }.to_json)
336
368
  @watch_req = stub_request(:post, api_url).
337
- with(body: { format: "json", token: "t123", action: "watch", titles: "Test" })
369
+ with(body: { format: 'json', token: 't123', action: 'watch', titles: 'Test' })
338
370
  end
339
371
 
340
- it "sends a valid watch request" do
341
- subject.watch_page("Test")
372
+ it 'sends a valid watch request' do
373
+ subject.watch_page('Test')
342
374
  expect(@watch_req).to have_been_requested
343
375
  end
344
376
  end
345
377
 
346
- describe "#unwatch_page" do
378
+ describe '#unwatch_page' do
347
379
  before do
348
380
  stub_request(:get, api_url).
349
- with(query: { format: "json", action: "tokens", type: "watch" }).
350
- to_return(body: { tokens: { watchtoken: "t123" } }.to_json )
381
+ with(query: { format: 'json', action: 'tokens', type: 'watch' }).
382
+ to_return(body: { tokens: { watchtoken: 't123' } }.to_json)
351
383
  @watch_req = stub_request(:post, api_url).
352
- with(body: { format: "json", token: "t123", action: "watch", titles: "Test", unwatch: "true" })
384
+ with(body: { format: 'json', token: 't123', action: 'watch',
385
+ titles: 'Test', unwatch: 'true' })
353
386
  end
354
387
 
355
- it "sends a valid unwatch request" do
356
- subject.unwatch_page("Test")
388
+ it 'sends a valid unwatch request' do
389
+ subject.unwatch_page('Test')
357
390
  expect(@watch_req).to have_been_requested
358
391
  end
359
392
  end