namely 0.0.1

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.
Files changed (68) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +3 -0
  3. data/.travis.yml +5 -0
  4. data/Gemfile +3 -0
  5. data/Gemfile.lock +51 -0
  6. data/LICENSE +22 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +125 -0
  9. data/Rakefile +16 -0
  10. data/lib/namely.rb +114 -0
  11. data/lib/namely/authenticator.rb +164 -0
  12. data/lib/namely/country.rb +9 -0
  13. data/lib/namely/currency_type.rb +9 -0
  14. data/lib/namely/event.rb +9 -0
  15. data/lib/namely/exceptions.rb +13 -0
  16. data/lib/namely/field.rb +9 -0
  17. data/lib/namely/job_tier.rb +9 -0
  18. data/lib/namely/profile.rb +13 -0
  19. data/lib/namely/report.rb +9 -0
  20. data/lib/namely/resource_gateway.rb +81 -0
  21. data/lib/namely/restful_model.rb +150 -0
  22. data/lib/namely/version.rb +3 -0
  23. data/namely.gemspec +30 -0
  24. data/spec/fixtures/vcr_cassettes/country_head.yml +51 -0
  25. data/spec/fixtures/vcr_cassettes/country_head_missing.yml +50 -0
  26. data/spec/fixtures/vcr_cassettes/country_index.yml +121 -0
  27. data/spec/fixtures/vcr_cassettes/country_show.yml +68 -0
  28. data/spec/fixtures/vcr_cassettes/country_show_missing.yml +54 -0
  29. data/spec/fixtures/vcr_cassettes/currencytype_index.yml +64 -0
  30. data/spec/fixtures/vcr_cassettes/event_head.yml +51 -0
  31. data/spec/fixtures/vcr_cassettes/event_head_missing.yml +50 -0
  32. data/spec/fixtures/vcr_cassettes/event_index.yml +88 -0
  33. data/spec/fixtures/vcr_cassettes/event_show.yml +62 -0
  34. data/spec/fixtures/vcr_cassettes/event_show_missing.yml +54 -0
  35. data/spec/fixtures/vcr_cassettes/field_index.yml +207 -0
  36. data/spec/fixtures/vcr_cassettes/jobtier_index.yml +103 -0
  37. data/spec/fixtures/vcr_cassettes/profile_create.yml +85 -0
  38. data/spec/fixtures/vcr_cassettes/profile_create_failed.yml +54 -0
  39. data/spec/fixtures/vcr_cassettes/profile_head.yml +51 -0
  40. data/spec/fixtures/vcr_cassettes/profile_head_missing.yml +50 -0
  41. data/spec/fixtures/vcr_cassettes/profile_index.yml +979 -0
  42. data/spec/fixtures/vcr_cassettes/profile_show.yml +91 -0
  43. data/spec/fixtures/vcr_cassettes/profile_show_missing.yml +54 -0
  44. data/spec/fixtures/vcr_cassettes/profile_show_updated.yml +91 -0
  45. data/spec/fixtures/vcr_cassettes/profile_update.yml +95 -0
  46. data/spec/fixtures/vcr_cassettes/profile_update_revert.yml +95 -0
  47. data/spec/fixtures/vcr_cassettes/report_head.yml +51 -0
  48. data/spec/fixtures/vcr_cassettes/report_head_missing.yml +50 -0
  49. data/spec/fixtures/vcr_cassettes/report_show.yml +185 -0
  50. data/spec/fixtures/vcr_cassettes/report_show_missing.yml +54 -0
  51. data/spec/fixtures/vcr_cassettes/token.yml +57 -0
  52. data/spec/fixtures/vcr_cassettes/token_refresh.yml +57 -0
  53. data/spec/namely/authenticator_spec.rb +143 -0
  54. data/spec/namely/configuration_spec.rb +33 -0
  55. data/spec/namely/country_spec.rb +11 -0
  56. data/spec/namely/currency_type_spec.rb +5 -0
  57. data/spec/namely/event_spec.rb +11 -0
  58. data/spec/namely/field_spec.rb +5 -0
  59. data/spec/namely/job_tier_spec.rb +5 -0
  60. data/spec/namely/profile_spec.rb +25 -0
  61. data/spec/namely/report_spec.rb +8 -0
  62. data/spec/namely/resource_gateway_spec.rb +93 -0
  63. data/spec/shared_examples/a_model_with_a_create_action.rb +24 -0
  64. data/spec/shared_examples/a_model_with_a_show_action.rb +38 -0
  65. data/spec/shared_examples/a_model_with_an_index_action.rb +17 -0
  66. data/spec/shared_examples/a_model_with_an_update_action.rb +37 -0
  67. data/spec/spec_helper.rb +49 -0
  68. metadata +280 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c6c9e3f2c619a62f8ecf845c656d50ff12f025d9
4
+ data.tar.gz: e17424ee887917ba69dd1928557bb6cd5c64ab02
5
+ SHA512:
6
+ metadata.gz: 2a9a3dc86ec13b62778feca282add92456013a154dfc8fa5e652a761a9f58350e35ee626184b5bf4aaf60cc47a9d58f56e6aeef156f6204ae16bc58ebdb11a0a
7
+ data.tar.gz: 5a025299aad278901b62a0fc0a24a4977cb4c56aaee28fcbc93f85c65811e70598c400366a487e872f8a78c2d8a19a4f65baecb577e4de15404d7375da07d297
@@ -0,0 +1,3 @@
1
+ .env
2
+ .yardoc
3
+ doc
@@ -0,0 +1,5 @@
1
+ rvm:
2
+ - 1.9.3
3
+ - jruby-19mode
4
+ - 2.0.0
5
+ - 2.1.3
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
@@ -0,0 +1,51 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ namely (0.0.1)
5
+ backports
6
+ rest_client
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ addressable (2.3.6)
12
+ backports (3.6.3)
13
+ crack (0.4.2)
14
+ safe_yaml (~> 1.0.0)
15
+ diff-lcs (1.2.5)
16
+ dotenv (1.0.2)
17
+ netrc (0.7.9)
18
+ rake (10.3.2)
19
+ rest_client (1.8.1)
20
+ netrc (~> 0.7.7)
21
+ rspec (3.1.0)
22
+ rspec-core (~> 3.1.0)
23
+ rspec-expectations (~> 3.1.0)
24
+ rspec-mocks (~> 3.1.0)
25
+ rspec-core (3.1.7)
26
+ rspec-support (~> 3.1.0)
27
+ rspec-expectations (3.1.2)
28
+ diff-lcs (>= 1.2.0, < 2.0)
29
+ rspec-support (~> 3.1.0)
30
+ rspec-mocks (3.1.3)
31
+ rspec-support (~> 3.1.0)
32
+ rspec-support (3.1.2)
33
+ safe_yaml (1.0.4)
34
+ vcr (2.9.3)
35
+ webmock (1.8.7)
36
+ addressable (>= 2.2.7)
37
+ crack (>= 0.1.7)
38
+ yard (0.8.7.6)
39
+
40
+ PLATFORMS
41
+ ruby
42
+
43
+ DEPENDENCIES
44
+ bundler (~> 1.7)
45
+ dotenv
46
+ namely!
47
+ rake (~> 10.0)
48
+ rspec
49
+ vcr
50
+ webmock
51
+ yard
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Namely
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,125 @@
1
+ # Namely
2
+
3
+ [![Travis](https://travis-ci.org/namely/ruby-client.svg?branch=master)](https://travis-ci.org/namely/ruby-client/builds)
4
+ [![Code Climate](https://codeclimate.com/github/namely/ruby-client/badges/gpa.svg)](https://codeclimate.com/github/namely/ruby-client)
5
+
6
+ TODO: Write a gem description
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ ```ruby
13
+ gem "namely"
14
+ ```
15
+
16
+ And then execute:
17
+
18
+ $ bundle
19
+
20
+ Or install it yourself as:
21
+
22
+ $ gem install namely
23
+
24
+ ## Configuration
25
+
26
+ You'll need to configure the gem to use your Namely account. Do this
27
+ by setting the `access_token` and `subdomain` variables in a
28
+ configuration block:
29
+
30
+ ```ruby
31
+ Namely.configure do |config|
32
+ config.access_token = "your_access_token"
33
+ config.subdomain = "your-organization"
34
+ end
35
+ ```
36
+
37
+ An access token can be obtained through your organization's Namely
38
+ account.
39
+
40
+ Namely associates a subdomain with your
41
+ organization. organization. For example, if your account is at
42
+ `http://your-organization.namely.com/`, your subdomain would be
43
+ `"your-organization"`.
44
+
45
+ In a Rails application this configuration belongs in
46
+ `config/initializers/namely.rb`.
47
+
48
+ ## Usage Examples
49
+
50
+ ```ruby
51
+ Namely::Country.all.each do |country|
52
+ puts "#{country.id} - #{country.name}"
53
+ end
54
+ # AF - Afghanistan
55
+ # AL - Albania
56
+ # DZ - Algeria
57
+ # AS - American Samoa
58
+ # ...
59
+ ```
60
+
61
+ ```ruby
62
+ if Namely::Country.exists?("BE")
63
+ "Belgium exists!"
64
+ else
65
+ "Hmm."
66
+ end # => "Belgium exists!"
67
+ ```
68
+
69
+ ```ruby
70
+ Namely::Country.find("BE")
71
+ # => <Namely::Country id="BE", name="Belgium", subdivision_type="Province", links={"subdivisions"=>[{"id"=>"BRU", "name"=>"Brussels"}, {"id"=>"VAN", "name"=>"Antwerpen (nl)"}, {"id"=>"VBR", "name"=>"Vlaams Brabant (nl)"}, {"id"=>"VLI", "name"=>"Limburg (nl)"}, {"id"=>"VOV", "name"=>"Oost-Vlaanderen (nl)"}, {"id"=>"VWV", "name"=>"West-Vlaanderen (nl)"}, {"id"=>"WBR", "name"=>"Brabant Wallon (fr)"}, {"id"=>"WHT", "name"=>"Hainaut (fr)"}, {"id"=>"WLG", "name"=>"Liège (fr)"}, {"id"=>"WLX", "name"=>"Luxembourg (fr)"}, {"id"=>"WNA", "name"=>"Namur (fr)"}]}>
72
+ ```
73
+
74
+ ```ruby
75
+ foo_bar = Namely::Profile.create!(
76
+ first_name: "Metasyntactic",
77
+ last_name: "Variable",
78
+ email: "foo_bar@namely.com"
79
+ )
80
+
81
+ foo_bar.id # => "37c919e2-f1c8-4beb-b1d4-a9a36ccc830c"
82
+ ```
83
+
84
+ ## Contributing
85
+
86
+ Wanna help out? Great! Here are a few resources that might help you
87
+ started:
88
+
89
+ * Documentation on [Namely's HTTP API]
90
+ * Namely tries to stick to the [JSON API standard]. If we're seriously
91
+ deviating from it, that may well be a bug.
92
+ * For coding style, we like [thoughtbot's style guide]
93
+
94
+ ### Setting up a development environment
95
+
96
+ The Namely gem uses [dotenv] to manage environment variables. To run
97
+ the tests you'll need to create a `.env` file in the project's root
98
+ directory and assign a few variables in it.
99
+
100
+ Just take this example `.env` file and plug in appropriate values:
101
+
102
+ ```
103
+ TEST_ACCESS_TOKEN=my-sample-access-token
104
+ TEST_SUBDOMAIN=my-sample-subdomain
105
+ ```
106
+
107
+ You'll need admin access to a Namely site to get tokens for these
108
+ variables. You can create application and permanent tokens on the API
109
+ page of the site.
110
+
111
+ ### Submitting your changes
112
+
113
+ 1. [Fork it!]
114
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
115
+ 3. Commit your changes (`git commit -am "Add some feature"`). Be sure
116
+ to include tests!
117
+ 4. Push to the branch (`git push origin my-new-feature`)
118
+ 5. Create a new pull request. We'll review it and leave feedback for
119
+ you ASAP.
120
+
121
+ [Namely's HTTP API]: http://namely.readme.io/v1/docs
122
+ [thoughtbot's style guide]: https://github.com/thoughtbot/guides/tree/master/style
123
+ [JSON API standard]: http://jsonapi.org/
124
+ [dotenv]: https://github.com/bkeepers/dotenv
125
+ [Fork it!]: https://github.com/namely/ruby-client/fork
@@ -0,0 +1,16 @@
1
+ require "rspec/core/rake_task"
2
+ require "bundler/gem_tasks"
3
+
4
+ RSpec::Core::RakeTask.new(:spec) do |task|
5
+ task.rspec_opts = ["--color"]
6
+ end
7
+
8
+ task :console do
9
+ exec "irb -r namely -I ./lib"
10
+ end
11
+
12
+ task :documentation do
13
+ exec "yardoc --no-private"
14
+ end
15
+
16
+ task default: :spec
@@ -0,0 +1,114 @@
1
+ require "backports"
2
+ require "json"
3
+ require "ostruct"
4
+ require "rest_client"
5
+
6
+ require "namely/authenticator"
7
+ require "namely/exceptions"
8
+ require "namely/resource_gateway"
9
+ require "namely/restful_model"
10
+
11
+ require "namely/country"
12
+ require "namely/currency_type"
13
+ require "namely/event"
14
+ require "namely/field"
15
+ require "namely/job_tier"
16
+ require "namely/profile"
17
+ require "namely/report"
18
+
19
+ require "namely/version"
20
+
21
+ module Namely
22
+ class << self
23
+ attr_writer :configuration
24
+ end
25
+
26
+ # Return the current configuration.
27
+ #
28
+ # @raise [ImproperlyConfiguredError] if the subdomain or access token
29
+ # haven't been configured.
30
+ #
31
+ # @return [Configuration]
32
+ def self.configuration
33
+ @configuration || raise(
34
+ ImproperlyConfiguredError,
35
+ "Before using the Namely gem, you'll need to configure it with `Namely.configure`."
36
+ )
37
+ end
38
+
39
+ # Set the configuration variables (the subdomain and access token)
40
+ # that allow the Namely gem to access your account.
41
+ #
42
+ # @yieldparam [Configuration] configuration the Configuration
43
+ # object, the attributes of which can be set in the block.
44
+ #
45
+ # @example
46
+ # Namely.configure do |config|
47
+ # config.access_token = "your_access_token"
48
+ # config.subdomain = "your-organization"
49
+ # end
50
+ #
51
+ # @return [void]
52
+ def self.configure
53
+ @configuration ||= Configuration.new
54
+ yield configuration
55
+ end
56
+
57
+ # Return a resource gateway for interfacing with a given resource.
58
+ #
59
+ # @param [String] resource_name
60
+ # @param [String] endpoint
61
+ #
62
+ # @return [ResourceGateway]
63
+ def self.resource_gateway(resource_name, endpoint)
64
+ configuration.resource_gateway(resource_name, endpoint)
65
+ end
66
+
67
+ class Configuration
68
+ attr_writer :access_token, :subdomain
69
+
70
+ # Get the access token.
71
+ #
72
+ # @raise [ImproperlyConfiguredError] if the access token hasn't
73
+ # been configured.
74
+ #
75
+ # @return [String] the access token
76
+ def access_token
77
+ @access_token || raise_missing_variable_error(:access_token)
78
+ end
79
+
80
+ # Get the subdomain.
81
+ #
82
+ # @raise [ImproperlyConfiguredError] if the subdomain hasn't been
83
+ # configured.
84
+ #
85
+ # @return [String] the subdomain
86
+ def subdomain
87
+ @subdomain || raise_missing_variable_error(:subdomain)
88
+ end
89
+
90
+ # Create a resource gateway for interfacing with a given resource.
91
+ #
92
+ # @param [String] resource_name
93
+ # @param [String] endpoint
94
+ #
95
+ # @return [ResourceGateway]
96
+ def resource_gateway(resource_name, endpoint)
97
+ Namely::ResourceGateway.new(
98
+ access_token: access_token,
99
+ endpoint: endpoint,
100
+ resource_name: resource_name,
101
+ subdomain: subdomain
102
+ )
103
+ end
104
+
105
+ private
106
+
107
+ def raise_missing_variable_error(variable)
108
+ raise(
109
+ ImproperlyConfiguredError,
110
+ "The Namely `#{variable}` configuration variable hasn't been set... did you set it when you called `Namely.configure`?"
111
+ )
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,164 @@
1
+ require "cgi"
2
+
3
+ module Namely
4
+ class Authenticator
5
+ # Return a new Authenticator instance.
6
+ #
7
+ # @param [Hash] options
8
+ # @option options [String] client_id
9
+ # @option options [String] client_secret
10
+ #
11
+ # @example
12
+ # authenticator = Authenticator.new(
13
+ # client_id: "my-client-id",
14
+ # client_secret: "my-client-secret"
15
+ # )
16
+ #
17
+ # @return [Authenticator]
18
+ def initialize(options)
19
+ @client_id = options.fetch(:client_id)
20
+ @client_secret = options.fetch(:client_secret)
21
+ end
22
+
23
+ # Returns a URL to begin the authorization code workflow. If you
24
+ # provide a redirect_uri you can receive the server's response.
25
+ #
26
+ # @param [Hash] options
27
+ # @option options [String] host (optional)
28
+ # @option options [String] protocol (optional, defaults to "https")
29
+ # @option options [String] subdomain (required)
30
+ # @option options [String] redirect_uri (optional)
31
+ #
32
+ # @return [String]
33
+ def authorization_code_url(options)
34
+ URL.new(options.merge(
35
+ path: "/api/v1/oauth2/authorize",
36
+ params: {
37
+ response_type: "code",
38
+ approve: "true",
39
+ client_id: client_id,
40
+ },
41
+ )).to_s
42
+ end
43
+
44
+ # Request an access token and refresh token.
45
+ #
46
+ # @param [Hash] options
47
+ # @option options [String] code (required)
48
+ # @option options [String] redirect_uri (required)
49
+ # @option options [String] subdomain (required)
50
+ #
51
+ # @example
52
+ # authenticator = Authenticator.new(
53
+ # client_id: "my-client-id",
54
+ # client_secret: "my-client-secret"
55
+ # )
56
+ #
57
+ # tokens = authenticator.retrieve_tokens(
58
+ # code: "my-code",
59
+ # subdomain: "my-subdomain",
60
+ # redirect_uri: "my-redirect-uri"
61
+ # )
62
+ #
63
+ # tokens["access_token"] # => "my-access-token"
64
+ # tokens["refresh_token"] # => "my-refresh-token"
65
+ # tokens["expires_in"] # => 1234
66
+ # tokens["token_type"] # => "bearer"
67
+ #
68
+ # @return [Hash]
69
+ def retrieve_tokens(options)
70
+ request_tokens(options, "authorization_code", options.fetch(:code))
71
+ end
72
+
73
+ # Get an updated access token using the refresh token.
74
+ #
75
+ # @param [Hash] options
76
+ # @option options [String] redirect_uri (required)
77
+ # @option options [String] refresh_token (required)
78
+ # @option options [String] subdomain (required)
79
+ #
80
+ # @example
81
+ # authenticator = Authenticator.new(
82
+ # client_id: "my-client-id",
83
+ # client_secret: "my-client-secret"
84
+ # )
85
+ #
86
+ # tokens = authenticator.refresh_access_token(
87
+ # redirect_uri: "my-redirect-uri",
88
+ # refresh_token: "my-refresh-token",
89
+ # subdomain: "my-subdomain"
90
+ # )
91
+ #
92
+ # tokens["access_token"] # => "my-access-token"
93
+ # tokens["expires_in"] # => 1234
94
+ # tokens["token_type"] # => "bearer"
95
+ #
96
+ # @return [Hash]
97
+ def refresh_access_token(options)
98
+ request_tokens(options, "refresh_token", options.fetch(:refresh_token))
99
+ end
100
+
101
+ private
102
+
103
+ attr_reader :client_id, :client_secret
104
+
105
+ def request_tokens(options, grant_type, token)
106
+ response = RestClient.post(
107
+ URL.new(options.merge(path: "/api/v1/oauth2/token")).to_s,
108
+ grant_type: grant_type,
109
+ client_id: client_id,
110
+ client_secret: client_secret,
111
+ refresh_token: token,
112
+ )
113
+ JSON.parse(response)
114
+ end
115
+
116
+ class URL
117
+ def initialize(options)
118
+ @options = options
119
+ end
120
+
121
+ def to_s
122
+ "#{protocol}://#{host}#{path}?#{query}"
123
+ end
124
+
125
+ private
126
+
127
+ attr_reader :options
128
+
129
+ def protocol
130
+ options.fetch(:protocol, "https")
131
+ end
132
+
133
+ def host
134
+ if options.has_key?(:subdomain)
135
+ "#{options[:subdomain]}.namely.com"
136
+ else
137
+ options.fetch(:host)
138
+ end
139
+ end
140
+
141
+ def path
142
+ options.fetch(:path)
143
+ end
144
+
145
+ def query
146
+ params.
147
+ map { |key, value| "#{CGI.escape(key.to_s)}=#{CGI.escape(value)}" }.
148
+ join("&")
149
+ end
150
+
151
+ def params
152
+ options.fetch(:params, {}).merge(redirect_uri_param)
153
+ end
154
+
155
+ def redirect_uri_param
156
+ if options.has_key?(:redirect_uri)
157
+ { redirect_uri: options[:redirect_uri] }
158
+ else
159
+ {}
160
+ end
161
+ end
162
+ end
163
+ end
164
+ end