clientele 0.0.1

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: 7094f398e0787615dcaad936f4886c7ef0d06e76
4
+ data.tar.gz: 3ba778bd32cf86045ff472fa3658660833920261
5
+ SHA512:
6
+ metadata.gz: dd64329a884aecaa05ba59ea9307bca73c556464c828926a9a888a7c3e4da3cb2bc64c7f6e386877e92c815d8b1d042a74fcacd38f130abb074af232a0a4c18d
7
+ data.tar.gz: ff01128ff6dca97923c7166ecd8c567b45f36d8a3eb88345a08eb149d8a9bf1fd6c46d5c6b8e469b83410720bec31178fe4861081a642762f0f416c70c52aacd
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in clientele.gemspec
4
+ gemspec
data/LICENSE.md ADDED
@@ -0,0 +1,25 @@
1
+ > **Copyright (c) 2014 Chris Keele**
2
+
3
+ MIT License
4
+ -----------
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining
7
+ a copy of this software and associated documentation files (the
8
+ "Software"), to deal in the Software without restriction, including
9
+ without limitation the rights to use, copy, modify, merge, publish,
10
+ distribute, sublicense, and/or sell copies of the Software, and to
11
+ permit persons to whom the Software is furnished to do so, subject to
12
+ the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be
15
+ included in all copies or substantial portions of the Software.
16
+
17
+ *
18
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25
+ *
data/README.md ADDED
@@ -0,0 +1,30 @@
1
+ Clientele
2
+ =========
3
+
4
+ > *DSL for creating RESTful API clients for external services.*
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ gem 'clientele'
11
+
12
+ And then execute:
13
+
14
+ $ bundle
15
+
16
+ Or install it yourself as:
17
+
18
+ $ gem install clientele
19
+
20
+ ## Usage
21
+
22
+ TODO: Write usage instructions here
23
+
24
+ ## Contributing
25
+
26
+ 1. Fork it ( https://github.com/[my-github-username]/clientele/fork )
27
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
28
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
29
+ 4. Push to the branch (`git push origin my-new-feature`)
30
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
data/clientele.gemspec ADDED
@@ -0,0 +1,37 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'clientele/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "clientele"
8
+ spec.version = Clientele::VERSION
9
+ spec.authors = ["Chris Keele"]
10
+ spec.email = ["dev@chriskeele.com"]
11
+ spec.summary = 'DSL for creating RESTful API clients for external services.'
12
+ spec.description = <<-DESC
13
+ DSL for creating RESTful API clients for external services.
14
+
15
+
16
+ DESC
17
+ spec.homepage = "https://github.com/christhekeele/clientele"
18
+ spec.license = "MIT"
19
+
20
+ spec.files = `git ls-files -z`.split("\x0")
21
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
22
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
23
+ spec.require_paths = ["lib"]
24
+
25
+ spec.required_ruby_version = '~> 2.0'
26
+
27
+ spec.add_dependency "faraday", "~> 0.9.0"
28
+ spec.add_dependency "faraday_middleware", "~> 0.9.0"
29
+ spec.add_dependency "rash", "~> 0.4.0"
30
+ spec.add_dependency "block_party", ">= 0.1.2"
31
+ spec.add_dependency "activesupport", "> 3.2"
32
+
33
+ spec.add_development_dependency "bundler", "> 1.3"
34
+ spec.add_development_dependency "rake"
35
+ spec.add_development_dependency "rspec", "~> 2.14"
36
+ spec.add_development_dependency "pry"
37
+ end
@@ -0,0 +1,85 @@
1
+ require 'forwardable'
2
+
3
+ require 'block_party'
4
+ require 'active_support/core_ext/class/attribute'
5
+
6
+ require 'clientele/configuration'
7
+ require 'clientele/request_builder'
8
+ require 'clientele/request'
9
+ require 'clientele/resource'
10
+
11
+ module Clientele
12
+ class API
13
+
14
+ extend BlockParty::Configurable
15
+ configure_with Configuration
16
+
17
+ extend SingleForwardable
18
+ def_delegator :configuration, :logger
19
+ def_delegators :request, *Request::VERBS
20
+
21
+ class_attribute :resources, instance_predicate: false
22
+ self.resources = {}
23
+
24
+ class << self
25
+
26
+ def client(opts={})
27
+ autoconfigure!
28
+ @client ||= new(opts)
29
+ end
30
+
31
+ def logger
32
+ autoconfigure!
33
+ configuration.logger
34
+ end
35
+
36
+ def resource(klass)
37
+ self.resources = resources.merge(:"#{klass}" => klass)
38
+ end
39
+
40
+ private
41
+
42
+ def autoconfigure!
43
+ self.configure unless configuration
44
+ end
45
+
46
+ def respond_to_missing?(method_name, include_private=false)
47
+ client.respond_to? method_name, include_private
48
+ end
49
+
50
+ def method_missing(method_name, *args, &block)
51
+ autoconfigure!
52
+ if respond_to_missing?(method_name, false)
53
+ client.send method_name, *args, &block
54
+ else; super; end
55
+ end
56
+
57
+ end
58
+
59
+ def initialize(opts={})
60
+ self.extend BlockParty::Configurable
61
+ self.configure_with self.class.configuration.class
62
+ self.configuration = self.class.configuration.clone
63
+ self.configuration.load_hash opts
64
+ end
65
+
66
+ protected
67
+
68
+ def request
69
+ Request
70
+ end
71
+
72
+ private
73
+
74
+ def respond_to_missing?(method_name, include_private=false)
75
+ resources.keys.include?(method_name) or super
76
+ end
77
+
78
+ def method_missing(method_name, *args, &block)
79
+ if resources.keys.include? method_name
80
+ RequestBuilder.new(self.class::resources[method_name], client: self)
81
+ else; super; end
82
+ end
83
+
84
+ end
85
+ end
@@ -0,0 +1,17 @@
1
+ require 'logger'
2
+
3
+ require 'faraday'
4
+
5
+ module Clientele
6
+ class Configuration < BlockParty::Configuration
7
+
8
+ attr_accessor :logger, :adapter, :headers, :root_url
9
+
10
+ def initialize
11
+ self.logger = Logger.new($stdout)
12
+ self.adapter = Faraday.default_adapter
13
+ self.headers = {}
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,3 @@
1
+ module Clientele
2
+ class ConfigurationError < RuntimeError; end
3
+ end
@@ -0,0 +1,97 @@
1
+ require 'json'
2
+
3
+ require 'faraday'
4
+ require 'faraday_middleware'
5
+ require 'rash'
6
+
7
+ require 'clientele/utils'
8
+
9
+ module Clientele
10
+ class Request < Struct.new(:verb, :path, :query, :body, :headers, :options, :callback, :resource)
11
+ include Clientele::Utils
12
+
13
+ VERBS = %i[get post put patch delete]
14
+ VERBS.each do |verb|
15
+ define_singleton_method verb do |opts={}, &callback|
16
+ new opts.merge(verb: __method__, callback: callback)
17
+ end
18
+ end
19
+
20
+ def initialize(props={})
21
+ apply self.class.defaults
22
+ apply props
23
+ end
24
+
25
+ def async?
26
+ !!callback
27
+ end
28
+
29
+ def url
30
+ ensure_trailing_slash merge_paths options[:root_url], path
31
+ end
32
+
33
+ def to_request(options={})
34
+ tap do |request|
35
+ request.options.deep_merge! options
36
+ end
37
+ end
38
+
39
+ def call
40
+ callback ? callback.call(response) : response
41
+ end
42
+ alias_method :~, :call
43
+
44
+ def + other
45
+ self.class.new(
46
+ verb: other.verb || verb,
47
+ path: merge_paths(path, other.path),
48
+ query: query.merge(other.query),
49
+ body: body.merge(other.body),
50
+ headers: headers.merge(other.headers),
51
+ options: options.merge(other.options),
52
+ callback: other.callback || callback,
53
+ resource: other.resource || resource,
54
+ )
55
+ end
56
+
57
+ private
58
+
59
+ def response
60
+ faraday_client.send(verb, ensure_trailing_slash(path)) do |request|
61
+ request.headers = options.fetch(:headers, {}).merge(headers)
62
+ request.params = deep_camelize_keys(query)
63
+ request.body = JSON.dump(deep_camelize_keys(body))
64
+ end
65
+ end
66
+
67
+ def faraday_client
68
+ Faraday.new(options[:root_url]) do |conn|
69
+ conn.response :rashify
70
+ conn.response :json, content_type: /\bjson$/, preserve_raw: true
71
+ conn.adapter options[:adapter]
72
+ end
73
+ end
74
+
75
+ class << self
76
+ def defaults
77
+ {
78
+ verb: :get,
79
+ path: nil,
80
+ query: {},
81
+ body: {},
82
+ headers: {},
83
+ options: {},
84
+ callback: nil,
85
+ resource: nil,
86
+ }
87
+ end
88
+ end
89
+
90
+ def apply(hash={})
91
+ hash.each do |key, value|
92
+ self.send :"#{key}=", value
93
+ end
94
+ end
95
+
96
+ end
97
+ end
@@ -0,0 +1,75 @@
1
+ require 'clientele/api'
2
+ require 'clientele/utils'
3
+
4
+ module Clientele
5
+ class RequestBuilder
6
+ include Clientele::Utils
7
+
8
+ def initialize(*resources, client: API.client)
9
+ @stack = Array(resources)
10
+ @client = client
11
+ end
12
+
13
+ def call
14
+ build.call
15
+ end
16
+ alias_method :~, :call
17
+
18
+ protected
19
+
20
+ def build
21
+ @stack.map(&:to_request).inject(:+).to_request(@client.configuration.to_hash)
22
+ end
23
+
24
+ def to_a
25
+ @stack
26
+ end
27
+
28
+ # Compare values only, class doesn't matter
29
+ def == other
30
+ to_a.zip(other.to_a).all? do |mine, theirs|
31
+ mine == theirs
32
+ end
33
+ end
34
+
35
+ # Compare values only, class matters
36
+ def eql?(other)
37
+ if other.is_a?(RequestBuilder)
38
+ to_a.zip(other.to_a).all? do |mine, theirs|
39
+ mine.eql? theirs
40
+ end
41
+ end
42
+ end
43
+
44
+ # Compare classes or instances for case statements
45
+ def === other
46
+ unless other.is_a? Class
47
+ to_a == other.to_a if other.respond_to? :to_a
48
+ else
49
+ super
50
+ end
51
+ end
52
+
53
+ def to_s
54
+ merge_paths(@stack.map(&:to_s))
55
+ end
56
+
57
+ private
58
+
59
+ def method_missing(method_name, *args, &block)
60
+ if API::resources.keys.include? method_name
61
+ @stack << API::resources[method_name]
62
+ self
63
+ elsif @stack.last.respond_to? method_name, false
64
+ @stack = @stack[0..-2] << @stack.last.send(method_name, *args, &block)
65
+ self
66
+ else; super; end
67
+ end
68
+ def respond_to_missing?(method_name, include_private=false)
69
+ API::resources.keys.include?(method_name) \
70
+ or @stack.last.respond_to?(method_name, include_private) \
71
+ or super
72
+ end
73
+
74
+ end
75
+ end
@@ -0,0 +1,44 @@
1
+ require 'clientele/request'
2
+ require 'clientele/utils'
3
+
4
+ require 'active_support/core_ext/string/inflections'
5
+
6
+ module Clientele
7
+ class Resource
8
+ include Clientele::Utils
9
+
10
+ @subclasses = []
11
+
12
+ class << self
13
+ include Clientele::Utils
14
+ attr_reader :subclasses
15
+ attr_accessor :path
16
+
17
+ def request(verb, path='', query: {}, body: {}, options: {}, &callback)
18
+ Request.send(verb,
19
+ path: merge_paths(@path || to_s, path),
20
+ query: query,
21
+ body: body,
22
+ options: options,
23
+ resource: self,
24
+ &callback
25
+ )
26
+ end
27
+
28
+ def to_request(options={}, &callback)
29
+ request :get, options: options, callback: callback
30
+ end
31
+
32
+ def to_s
33
+ self.name.split('::').last.pluralize.underscore
34
+ end
35
+
36
+ private
37
+
38
+ def inherited(base)
39
+ @subclasses << base
40
+ end
41
+ end
42
+
43
+ end
44
+ end
@@ -0,0 +1,25 @@
1
+ require 'active_support/core_ext/hash'
2
+ require 'active_support/core_ext/object/blank'
3
+ require 'active_support/core_ext/string/inflections'
4
+
5
+ module Clientele
6
+ module Utils
7
+
8
+ module_function
9
+
10
+ def merge_paths(*urls)
11
+ ensure_trailing_slash urls.reject(&:blank?).join('/').sub(/(?<!:)\/+/, '/')
12
+ end
13
+
14
+ def ensure_trailing_slash(url)
15
+ url.end_with?('/') ? url : url + '/'
16
+ end
17
+
18
+ def deep_camelize_keys(hash)
19
+ hash.deep_transform_keys do |key|
20
+ key.to_s.camelize(:lower)
21
+ end
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,3 @@
1
+ module Clientele
2
+ VERSION = "0.0.1"
3
+ end
data/lib/clientele.rb ADDED
@@ -0,0 +1,6 @@
1
+ require "clientele/api"
2
+ require "clientele/version"
3
+
4
+ # module Clientele
5
+ #
6
+ # end
metadata ADDED
@@ -0,0 +1,188 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: clientele
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Chris Keele
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-07-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: faraday
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.9.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.9.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: faraday_middleware
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.9.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.9.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: rash
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.4.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.4.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: block_party
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 0.1.2
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: 0.1.2
69
+ - !ruby/object:Gem::Dependency
70
+ name: activesupport
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.2'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.2'
83
+ - !ruby/object:Gem::Dependency
84
+ name: bundler
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">"
88
+ - !ruby/object:Gem::Version
89
+ version: '1.3'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">"
95
+ - !ruby/object:Gem::Version
96
+ version: '1.3'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rake
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rspec
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '2.14'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '2.14'
125
+ - !ruby/object:Gem::Dependency
126
+ name: pry
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ description: |2+
140
+ DSL for creating RESTful API clients for external services.
141
+
142
+
143
+ email:
144
+ - dev@chriskeele.com
145
+ executables: []
146
+ extensions: []
147
+ extra_rdoc_files: []
148
+ files:
149
+ - ".gitignore"
150
+ - Gemfile
151
+ - LICENSE.md
152
+ - README.md
153
+ - Rakefile
154
+ - clientele.gemspec
155
+ - lib/clientele.rb
156
+ - lib/clientele/api.rb
157
+ - lib/clientele/configuration.rb
158
+ - lib/clientele/exceptions.rb
159
+ - lib/clientele/request.rb
160
+ - lib/clientele/request_builder.rb
161
+ - lib/clientele/resource.rb
162
+ - lib/clientele/utils.rb
163
+ - lib/clientele/version.rb
164
+ homepage: https://github.com/christhekeele/clientele
165
+ licenses:
166
+ - MIT
167
+ metadata: {}
168
+ post_install_message:
169
+ rdoc_options: []
170
+ require_paths:
171
+ - lib
172
+ required_ruby_version: !ruby/object:Gem::Requirement
173
+ requirements:
174
+ - - "~>"
175
+ - !ruby/object:Gem::Version
176
+ version: '2.0'
177
+ required_rubygems_version: !ruby/object:Gem::Requirement
178
+ requirements:
179
+ - - ">="
180
+ - !ruby/object:Gem::Version
181
+ version: '0'
182
+ requirements: []
183
+ rubyforge_project:
184
+ rubygems_version: 2.2.2
185
+ signing_key:
186
+ specification_version: 4
187
+ summary: DSL for creating RESTful API clients for external services.
188
+ test_files: []