excon-hypermedia 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: cb36e9f12e5909b7e4d4321184f39582882da139
4
+ data.tar.gz: 3b6c39402e531bd8055c75a032aba0db380c6635
5
+ SHA512:
6
+ metadata.gz: 759eedec55a1da3d4aced37c4385dcafb38c44ba1e4636222398251808ac6972b52dc5962d471ee3547032605d204f7c2bff3e12d3395f1f6dfd82b3e41f08dc
7
+ data.tar.gz: 08371cb0d025c67159f85481d2e2ef61cea5b8cb08e0dbf28917cd434f3fccdfb84ceaa0cef2af56d1a2f818c7b9f426f0fdd005731abb4b6ca66fb6b9b65e97
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ /.bundle/
2
+ /.wercker/
3
+ /Gemfile.lock
4
+ /pkg/
data/.rubocop.yml ADDED
@@ -0,0 +1,7 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.3
3
+ Exclude:
4
+ - '.wercker/**/*'
5
+
6
+ Metrics/LineLength:
7
+ Max: 100
data/.wercker.yml ADDED
@@ -0,0 +1,7 @@
1
+ box: ruby:2.3
2
+ build:
3
+ steps:
4
+ - bundle-install
5
+ - script:
6
+ name: tests
7
+ code: bundle exec rake
@@ -0,0 +1,49 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, and in the interest of
4
+ fostering an open and welcoming community, we pledge to respect all people who
5
+ contribute through reporting issues, posting feature requests, updating
6
+ documentation, submitting pull requests or patches, and other activities.
7
+
8
+ We are committed to making participation in this project a harassment-free
9
+ experience for everyone, regardless of level of experience, gender, gender
10
+ identity and expression, sexual orientation, disability, personal appearance,
11
+ body size, race, ethnicity, age, religion, or nationality.
12
+
13
+ Examples of unacceptable behavior by participants include:
14
+
15
+ * The use of sexualized language or imagery
16
+ * Personal attacks
17
+ * Trolling or insulting/derogatory comments
18
+ * Public or private harassment
19
+ * Publishing other's private information, such as physical or electronic
20
+ addresses, without explicit permission
21
+ * Other unethical or unprofessional conduct
22
+
23
+ Project maintainers have the right and responsibility to remove, edit, or
24
+ reject comments, commits, code, wiki edits, issues, and other contributions
25
+ that are not aligned to this Code of Conduct, or to ban temporarily or
26
+ permanently any contributor for other behaviors that they deem inappropriate,
27
+ threatening, offensive, or harmful.
28
+
29
+ By adopting this Code of Conduct, project maintainers commit themselves to
30
+ fairly and consistently applying these principles to every aspect of managing
31
+ this project. Project maintainers who do not follow or enforce the Code of
32
+ Conduct may be permanently removed from the project team.
33
+
34
+ This code of conduct applies both within project spaces and in public spaces
35
+ when an individual is representing the project or its community.
36
+
37
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
38
+ reported by contacting a project maintainer at jean@mertz.fm. All
39
+ complaints will be reviewed and investigated and will result in a response that
40
+ is deemed necessary and appropriate to the circumstances. Maintainers are
41
+ obligated to maintain confidentiality with regard to the reporter of an
42
+ incident.
43
+
44
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
45
+ version 1.3.0, available at
46
+ [http://contributor-covenant.org/version/1/3/0/][version]
47
+
48
+ [homepage]: http://contributor-covenant.org
49
+ [version]: http://contributor-covenant.org/version/1/3/0/
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+ source 'https://rubygems.org'
3
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Jean Mertz
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
13
+ all 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
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,85 @@
1
+ # Excon::Hypermedia
2
+
3
+ Teaches [Excon][] how to talk to [HyperMedia APIs][hypermedia].
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'excon-hypermedia'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ ```shell
16
+ bundle
17
+ ```
18
+
19
+ Or install it yourself as:
20
+
21
+ ```shell
22
+ gem install excon-hypermedia
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ **NOTE**: This library is in very early development. Right now, it only talks
28
+ the `HAL/JSON` protocol, and it only knows how to follow (non-curie) link
29
+ relations. It returns raw response bodies in string format.
30
+
31
+ This gem adds a thin layer on top of [Excon][excon] to make it talk with an
32
+ HyperMedia-enabled API. To let Excon know the connection supports HyperMedia,
33
+ simply add the `hypermedia: true` option.
34
+
35
+ ```ruby
36
+ conn = Excon.new('http://www.example.com/api.json', hypermedia: true)
37
+ conn.class # => Excon::Connection
38
+ ```
39
+
40
+ From that point on, you can use this single connection to make all requests. The
41
+ `hypermedia` option will be passed on to all subsequent connection objects, as
42
+ long as you keep chaining the requests from the original top-level connection.
43
+
44
+ ```ruby
45
+ product = conn.product(expand: { uid: 'hello' })
46
+ product.class # => Excon::Connection
47
+
48
+ response = product.get
49
+ response.class # => Excon::Response
50
+ response.body.class # => String
51
+ ```
52
+
53
+ As seen above, you can expand URI Template variables using the `expand` option,
54
+ provided by the [`excon-addressable` library][excon-addressable].
55
+
56
+ You can mark any connection object as hypermedia-aware – not just the top-level
57
+ entrypoint – by passing in the `hypermedia: true` option:
58
+
59
+ ```ruby
60
+ user = Excon.new('http://www.example.com/users/jeanmertz', hypermedia: true)
61
+ user.orders.class # => Excon::Connection
62
+ ```
63
+
64
+ Since each new resource is simply an `Excon::Connection` object, all
65
+ [Excon-provided options][options] are available as well:
66
+
67
+ ```ruby
68
+ product.get(idempotent: true, retry_limit: 6)
69
+ ```
70
+
71
+ ## License
72
+
73
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
74
+
75
+ ## TODO
76
+
77
+ * use Excon's Middleware system
78
+ * make it easy to access attributes in response objects
79
+ * properly handle curied-links and/or non-valid Ruby method name links
80
+ * work correctly with Excon.get/post/delete shortcut methods
81
+
82
+ [excon]: https://github.com/excon/excon
83
+ [hypermedia]: https://en.wikipedia.org/wiki/HATEOAS
84
+ [excon-addressable]: https://github.com/JeanMertz/excon-addressable
85
+ [options]: https://github.com/excon/excon#options
data/Rakefile ADDED
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+ require 'bundler/gem_tasks'
3
+ require 'rake/testtask'
4
+ require 'rubocop/rake_task'
5
+
6
+ RuboCop::RakeTask.new do |t|
7
+ t.options = %w(--display-cop-names --extra-details --display-style-guide)
8
+ end
9
+
10
+ Rake::TestTask.new(:test) do |t|
11
+ t.libs << 'test'
12
+ t.libs << 'lib'
13
+ t.test_files = FileList['test/**/*_test.rb']
14
+ end
15
+
16
+ task default: %i(test rubocop)
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+ # coding: utf-8
3
+ lib = File.expand_path('../lib', __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'excon/hypermedia/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'excon-hypermedia'
9
+ spec.version = Excon::Hypermedia::VERSION
10
+ spec.authors = %w(Jean Mertz)
11
+ spec.email = %w(jean@mertz.fm)
12
+
13
+ spec.summary = 'Excon, with Hypermedia traversing baked in.'
14
+ spec.description = 'Excon, with Hypermedia traversing baked in.'
15
+ spec.homepage = 'https://github.com/JeanMertz/excon-hypermedia'
16
+ spec.license = 'MIT'
17
+ spec.files = `git ls-files -z`.split("\x0")
18
+ spec.require_paths = %w(lib)
19
+
20
+ spec.add_development_dependency 'bundler', '~> 1.12'
21
+ spec.add_development_dependency 'rake', '~> 10.0'
22
+ spec.add_development_dependency 'minitest', '~> 5.0'
23
+ spec.add_development_dependency 'rubocop', '~> 0.40'
24
+ spec.add_development_dependency 'pry', '~> 0.10'
25
+
26
+ spec.add_dependency 'excon', '~> 0.49'
27
+ spec.add_dependency 'excon-addressable', '~> 0.1'
28
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+ require 'excon'
3
+ require 'excon/addressable'
4
+ require 'excon/hypermedia/hyper_media'
5
+
6
+ # :nodoc:
7
+ module Excon
8
+ # HyperMedia addition to Excon.
9
+ #
10
+ module Hypermedia
11
+ def new(url, params = {})
12
+ params[:hypermedia] ? super.extend(HyperMedia) : super
13
+ end
14
+ end
15
+
16
+ singleton_class.prepend Hypermedia
17
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+ require 'json'
3
+
4
+ module Excon
5
+ # HyperMedia
6
+ #
7
+ module HyperMedia
8
+ def method_missing(method_name, *params)
9
+ return super unless (url = entrypoint.dig('_links', method_name.to_s, 'href'))
10
+
11
+ Excon.new(url, params.first.to_h.merge(hypermedia: true))
12
+ end
13
+
14
+ def respond_to_missing?(method_name, include_private = false)
15
+ entrypoint.dig('_links', method_name.to_s, 'href') ? true : super
16
+ end
17
+
18
+ private
19
+
20
+ def entrypoint
21
+ @entrypoint ||= JSON.parse(get.body)
22
+ rescue JSON::ParserError
23
+ {}
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+ module Excon
3
+ module Hypermedia
4
+ VERSION = '0.1.0'
5
+ end
6
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+ # rubocop:disable Metrics/AbcSize, Metrics/LineLength
3
+ require_relative '../test_helper'
4
+
5
+ module Excon
6
+ # HypermediaTest
7
+ #
8
+ # Verifies the Excon connection consuming HyperMedia APIs.
9
+ #
10
+ class HypermediaTest < Minitest::Test
11
+ def entrypoint
12
+ <<~EOF
13
+ {
14
+ "_links": {
15
+ "hello": {
16
+ "href":"http://www.example.com/hello/{location}"
17
+ }
18
+ }
19
+ }
20
+ EOF
21
+ end
22
+
23
+ def hello_world
24
+ <<~EOF
25
+ {
26
+ "_links": {
27
+ "goodbye": {
28
+ "href":"http://www.example.com/hello/world/goodbye{?message}"
29
+ }
30
+ }
31
+ }
32
+ EOF
33
+ end
34
+
35
+ def hello_universe
36
+ <<~EOF
37
+ {
38
+ "_links": {
39
+ "goodbye": {
40
+ "href":"http://www.example.com/hello/universe/goodbye{?message}"
41
+ }
42
+ }
43
+ }
44
+ EOF
45
+ end
46
+
47
+ def setup
48
+ Excon.defaults[:mock] = true
49
+
50
+ Excon.stub({ method: :get, path: '/api' }, body: entrypoint, status: 200)
51
+ Excon.stub({ method: :get, path: '/hello/world' }, body: hello_world, status: 200)
52
+ Excon.stub({ method: :get, path: '/hello/world/goodbye', query: nil }, body: 'bye!', status: 200)
53
+ Excon.stub({ method: :get, path: '/hello/world/goodbye', query: 'message=farewell' }, body: 'farewell', status: 200)
54
+ Excon.stub({ method: :get, path: '/hello/universe' }, body: hello_universe, status: 200)
55
+ end
56
+
57
+ def test_hypermedia_request
58
+ conn = Excon.new('http://www.example.com/api', hypermedia: true)
59
+ conn2 = conn.hello(expand: { location: 'world' })
60
+ conn3 = conn.hello(expand: { location: 'universe' })
61
+
62
+ assert_equal '/hello/world', conn2.data[:path]
63
+ assert conn2.get.body.include?('http://www.example.com/hello/world/goodbye{?message}')
64
+
65
+ assert_equal '/hello/universe', conn3.data[:path]
66
+ assert conn3.get.body.include?('http://www.example.com/hello/universe/goodbye{?message}')
67
+ end
68
+
69
+ def test_nested_hypermedia_request
70
+ conn = Excon.new('http://www.example.com/api', hypermedia: true)
71
+ conn2 = conn.hello(expand: { location: 'world' }).goodbye
72
+ conn3 = conn.hello(expand: { location: 'world' }).goodbye(expand: { message: 'farewell' })
73
+
74
+ assert_equal '/hello/world/goodbye', conn2.data[:path]
75
+ assert_nil conn2.data[:query]
76
+ assert_equal 'bye!', conn2.get.body
77
+
78
+ assert_equal '/hello/world/goodbye', conn3.data[:path]
79
+ assert_equal 'message=farewell', conn3.data[:query]
80
+ assert_equal 'farewell', conn3.get.body
81
+ end
82
+
83
+ def teardown
84
+ Excon.stubs.clear
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
3
+ require 'excon/hypermedia'
4
+ require 'minitest/autorun'
metadata ADDED
@@ -0,0 +1,158 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: excon-hypermedia
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Jean
8
+ - Mertz
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2016-05-17 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '1.12'
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '1.12'
28
+ - !ruby/object:Gem::Dependency
29
+ name: rake
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '10.0'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '10.0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: minitest
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '5.0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '5.0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: rubocop
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: '0.40'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '0.40'
70
+ - !ruby/object:Gem::Dependency
71
+ name: pry
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - "~>"
75
+ - !ruby/object:Gem::Version
76
+ version: '0.10'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: '0.10'
84
+ - !ruby/object:Gem::Dependency
85
+ name: excon
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - "~>"
89
+ - !ruby/object:Gem::Version
90
+ version: '0.49'
91
+ type: :runtime
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - "~>"
96
+ - !ruby/object:Gem::Version
97
+ version: '0.49'
98
+ - !ruby/object:Gem::Dependency
99
+ name: excon-addressable
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - "~>"
103
+ - !ruby/object:Gem::Version
104
+ version: '0.1'
105
+ type: :runtime
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - "~>"
110
+ - !ruby/object:Gem::Version
111
+ version: '0.1'
112
+ description: Excon, with Hypermedia traversing baked in.
113
+ email:
114
+ - jean@mertz.fm
115
+ executables: []
116
+ extensions: []
117
+ extra_rdoc_files: []
118
+ files:
119
+ - ".gitignore"
120
+ - ".rubocop.yml"
121
+ - ".wercker.yml"
122
+ - CODE_OF_CONDUCT.md
123
+ - Gemfile
124
+ - LICENSE.txt
125
+ - README.md
126
+ - Rakefile
127
+ - excon-hypermedia.gemspec
128
+ - lib/excon/hypermedia.rb
129
+ - lib/excon/hypermedia/hyper_media.rb
130
+ - lib/excon/hypermedia/version.rb
131
+ - test/excon/hypermedia_test.rb
132
+ - test/test_helper.rb
133
+ homepage: https://github.com/JeanMertz/excon-hypermedia
134
+ licenses:
135
+ - MIT
136
+ metadata: {}
137
+ post_install_message:
138
+ rdoc_options: []
139
+ require_paths:
140
+ - lib
141
+ required_ruby_version: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ required_rubygems_version: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - ">="
149
+ - !ruby/object:Gem::Version
150
+ version: '0'
151
+ requirements: []
152
+ rubyforge_project:
153
+ rubygems_version: 2.5.1
154
+ signing_key:
155
+ specification_version: 4
156
+ summary: Excon, with Hypermedia traversing baked in.
157
+ test_files: []
158
+ has_rdoc: