Skip to content

Commit 2dfa03b

Browse files
committed
Change params hash to stop having side effects.
1 parent a184993 commit 2dfa03b

2 files changed

Lines changed: 85 additions & 49 deletions

File tree

lib/github_api/params_hash.rb

Lines changed: 39 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,20 @@
44
require 'base64'
55

66
module Github
7-
87
# Class responsible for holding request parameters
98
class ParamsHash < DelegateClass(Hash)
109
include Normalizer
1110
include MimeType
1211

12+
REQUEST_PARAMS = [:accept, :media, :data, :raw, :content_type, :headers]
13+
1314
def initialize(hash)
1415
super(normalize!(Hash[hash]))
1516
end
1617

1718
# Create empty hash
1819
#
20+
# @api public
1921
def self.empty
2022
new({})
2123
end
@@ -24,52 +26,55 @@ def self.empty
2426
#
2527
# [.version].param[+json]
2628
#
29+
# @api public
2730
def media
2831
parse(delete('media'))
2932
end
3033

31-
# Return accept header if present
34+
# Get accept header
3235
#
36+
# @api public
3337
def accept
34-
if has_key?('accept')
35-
delete('accept')
36-
elsif has_key?('media')
37-
media
38-
else
39-
nil
38+
if key?('accept') then self['accept']
39+
elsif key?('media') then media
40+
else nil
4041
end
4142
end
4243

4344
# Extract request data from parameters
4445
#
46+
# @api public
4547
def data
46-
if has_key?('data') && !self['data'].nil?
47-
return delete('data')
48+
if key?('data') && !self['data'].nil?
49+
self['data']
4850
else
49-
return to_hash
51+
request_params
5052
end
5153
end
5254

5355
def encoder
54-
if has_key?('encoder') && self['encoder']
55-
return delete('encoder')
56+
if key?('encoder') && self['encoder']
57+
self['encoder']
5658
else
57-
return {}
59+
{}
5860
end
5961
end
6062

61-
# Any client configuration options
63+
# Configuration options from request
6264
#
65+
# @return [Hash]
66+
#
67+
# @api public
6368
def options
64-
opts = has_key?('options') ? delete('options') : {}
65-
headers = opts.fetch(:headers) { {} }
69+
opts = fetch('options', {})
70+
headers = fetch('headers', {})
6671
if value = accept
6772
headers[:accept] = value
6873
end
69-
if value = delete('content_type')
70-
headers[:content_type] = value
74+
if self['content_type']
75+
headers[:content_type] = self['content_type']
7176
end
72-
opts[:raw] = has_key?('raw') ? delete('raw') : false
77+
opts[:raw] = key?('raw') ? self['raw'] : false
7378
opts[:headers] = headers unless headers.empty?
7479
opts
7580
end
@@ -79,23 +84,32 @@ def options
7984
def merge_default(defaults)
8085
if defaults && !defaults.empty?
8186
defaults.each do |key, value|
82-
self[key] = value unless self.has_key?(key)
87+
self[key] = value unless self.key?(key)
8388
end
8489
end
8590
self
8691
end
8792

8893
# Base64 encode string removing newline characters
8994
#
95+
# @api public
9096
def strict_encode64(key)
9197
value = self[key]
9298
encoded = if Base64.respond_to?(:strict_encode64)
93-
Base64.strict_encode64(value)
94-
else
95-
[value].pack("m0")
96-
end
99+
Base64.strict_encode64(value)
100+
else
101+
[value].pack('m0')
102+
end
97103
self[key] = encoded.delete("\n\r")
98104
end
99105

106+
# Filter out request params
107+
#
108+
# @api public
109+
def request_params
110+
to_hash.select do |key, value|
111+
!REQUEST_PARAMS.include?(key.to_sym)
112+
end
113+
end
100114
end # ParamsHash
101115
end # Github

spec/github/params_hash_spec.rb

Lines changed: 46 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,74 +2,96 @@
22

33
require 'spec_helper'
44

5-
describe Github::ParamsHash do
5+
RSpec.describe Github::ParamsHash do
66
let(:object) { described_class }
77

8-
subject { object.new(hash) }
8+
subject(:params) { object.new(hash) }
99

10-
context 'converts key to string' do
11-
let(:hash) { {:foo => 123 }}
10+
context 'when empty' do
11+
let(:hash) { {} }
12+
13+
it { expect(params.options).to eq({:raw => false}) }
14+
15+
it { expect(params.data).to eq({}) }
1216

13-
it { expect(subject).to be_a(Github::ParamsHash)}
17+
it { expect(params.accept).to eq(nil) }
18+
end
19+
20+
context 'converts key to string' do
21+
let(:hash) { {foo: 123 } }
1422

15-
it { expect(subject['foo']).to eql(123) }
23+
it { expect(params['foo']).to eql(123) }
1624

17-
it { expect(subject.data).to eql({"foo" => 123}) }
25+
it { expect(params.data).to eql({"foo" => 123}) }
1826
end
1927

2028
context 'media' do
21-
let(:hash) { {:media => "raw"} }
29+
let(:hash) { {media: "raw"} }
2230

2331
it 'parses media key' do
24-
expect(subject.media).to eql('application/vnd.github.v3.raw+json')
32+
expect(params.media).to eql('application/vnd.github.v3.raw+json')
2533
end
2634
end
2735

2836
context 'with accept' do
29-
let(:hash) { {:accept => "application/json"} }
37+
let(:hash) { {accept: "application/json"} }
3038

3139
it 'overwrites media key' do
32-
expect(subject.accept).to eql('application/json')
40+
expect(params.accept).to eql('application/json')
41+
expect(params.to_hash).to eq({'accept' => 'application/json'})
3342
end
3443
end
3544

3645
context 'without accept' do
3746
let(:hash) { {} }
3847

3948
it 'overwrites media key' do
40-
expect(subject.accept).to be_nil
49+
expect(params.accept).to be_nil
4150
end
4251
end
4352

44-
context 'extract data' do
45-
let(:hash) { {:data => 'foobar'} }
53+
context 'when data' do
54+
let(:hash) { {data: 'foobar'} }
4655

47-
it { expect(subject.data).to eql('foobar') }
56+
it 'extracts data key' do
57+
expect(params.data).to eql('foobar')
58+
expect(params.to_hash).to eql({'data' => 'foobar'})
59+
end
60+
end
61+
62+
context 'when content_type' do
63+
let(:hash) { {content_type: 'application/octet-stream'} }
64+
65+
it 'extracts content_type key' do
66+
expect(params.options[:headers]).to eql(hash)
67+
end
4868
end
4969

50-
context 'extracts options headers' do
51-
let(:hash) { {:content_type => 'application/octet-stream'} }
70+
context 'when header' do
71+
let(:hash) { {headers: {"X-GitHub-OTP" => "<2FA token>"}} }
5272

53-
it { expect(subject.options[:headers]).to eql(hash) }
73+
it "sets headers" do
74+
expect(params.options[:headers]).to eql({"X-GitHub-OTP" => "<2FA token>" })
75+
end
5476
end
5577

5678
context 'merges defaults' do
5779
let(:hash) { { :homepage => "https://tty.github.io" }}
5880
let(:defaults) {
59-
{ "homepage" => "https://github.com",
60-
"private" => false}
81+
{ "homepage" => "https://github.com",
82+
"private" => false}
6183
}
6284

6385
it 'correctly updates values' do
6486
subject.merge_default(defaults)
65-
expect(subject['homepage']).to eql("https://tty.github.io")
66-
expect(subject['private']).to be_false
87+
expect(params['homepage']).to eql("https://tty.github.io")
88+
expect(params['private']).to be_false
6789
end
6890
end
6991

7092
context 'strict encode' do
71-
let(:hash) { { :content => "puts 'hello ruby'"} }
93+
let(:hash) { {content: "puts 'hello ruby'"} }
7294

73-
it { expect(subject.strict_encode64('content')).to eql('cHV0cyAnaGVsbG8gcnVieSc=') }
95+
it { expect(params.strict_encode64('content')).to eql('cHV0cyAnaGVsbG8gcnVieSc=') }
7496
end
7597
end

0 commit comments

Comments
 (0)