diesel-api-dsl 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,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ YWYzZDNhNzk5OWE5MDgxOWM1MzE4MjZlNDg2YWFjMDUyNzlmNjNkOQ==
5
+ data.tar.gz: !binary |-
6
+ ODhkYmQ4ZDFjMTBjZjZiMTUxMDU4NTI4NjhkMzQ5NTUzODg5OGQxNA==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ ZDViYzZlNzlkOGYyYWRiZDY1ZmNhZTRlM2U2OWQwNmJkZmQzZmI5OGEwMTZh
10
+ OWZlZjExOTA4NjRiN2E0MjZiZmE5N2JiYzJjMjFkMGQ1NWI5ZTQ0OGM0YTZk
11
+ M2M5MTk3ZWRiYzk1YTIxNTQ3ZDU2NzQ3MjQ2YjNmNDYyYWQ1ZmQ=
12
+ data.tar.gz: !binary |-
13
+ NjhlOGZkZTg2ODhiYmNkYjQ5MmYwZjVjYTRjOWZkNjhhZTI5NjZlMTUyYzhj
14
+ NjlhMzkyNTljZDQ1Y2IzZWM1YmRmODVlNzdlNDBkMmNjZWYyZmJlNjdhMzU1
15
+ MDJiYjk2NDFhNThhNTkzZTIwODRkYjE1Nzc4MTJhODgyZjQ4Yzc=
data/.gitignore ADDED
@@ -0,0 +1,18 @@
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
+ *.swp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in diesel.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Calvin Yu
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ 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, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # Diesel
2
+
3
+ Create API Clients From an DSL
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'diesel'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install diesel
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,55 @@
1
+ apis do
2
+ description "PivotalTracker API v5"
3
+ api_version "5"
4
+ base_path "https://www.pivotaltracker.com/services/v5"
5
+
6
+ api_key "X-TrackerToken" do
7
+ nickname :api_token
8
+ pass_as :header
9
+ end
10
+
11
+ header "Content-Type" => "application/json"
12
+
13
+ resource "projects/{project_id}/stories" do
14
+ description "Operations on stories"
15
+
16
+ operation :create_story do
17
+ reference_url "https://www.pivotaltracker.com/help/api/rest/v5#projects_project_id_stories_post"
18
+ method :post
19
+
20
+ parameter "project_id" do
21
+ param_type :path
22
+ required
23
+ end
24
+
25
+ body "story" do
26
+ data_type "NewStory"
27
+ required
28
+ end
29
+ end
30
+ end
31
+
32
+ complex_type "NewStory" do
33
+ required :name
34
+ property :cl_numbers
35
+ property :current_state do
36
+ enum :accepted, :delivered, :finished, :started, :rejected, :unstarted, :unscheduled
37
+ end
38
+ property :external_id
39
+ property :deadline
40
+ property :description
41
+ property :estimate
42
+ property :integration_id
43
+ property :planned_iteration_number
44
+ property :requested_by_id
45
+ property :accepted_at
46
+ property :before_id
47
+ property :created_at
48
+ property :after_id
49
+ property :name
50
+ property :story_type do
51
+ enum :feature, :bug, :chore, :release
52
+ end
53
+ end
54
+
55
+ end
data/apis/slack.rb ADDED
@@ -0,0 +1,36 @@
1
+ apis do
2
+ description "Slack"
3
+ api_version "1"
4
+ base_path "https://scoutmob.slack.com/services/hooks"
5
+
6
+ api_key "token" do
7
+ nickname :api_token
8
+ pass_as :query_parameter
9
+ end
10
+
11
+ header "Content-Type" => "application/json"
12
+
13
+ resource "incoming-webhook" do
14
+ description "Incoming Webhook"
15
+
16
+ operation :send_message do
17
+ reference_url "https://my.slack.com/services/new/incoming-webhook"
18
+ method :post
19
+
20
+ body "payload" do
21
+ data_type "Payload"
22
+ end
23
+ end
24
+ end
25
+
26
+ complex_type "Payload" do
27
+ required :text
28
+ property :text
29
+ property :channel
30
+ property :username
31
+ property :icon_url
32
+ property :icon_emoji
33
+ end
34
+
35
+ end
36
+
data/diesel.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'diesel/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "diesel-api-dsl"
8
+ spec.version = Diesel::VERSION
9
+ spec.authors = ["Calvin Yu"]
10
+ spec.email = ["me@sourcebender.com"]
11
+ spec.description = %q{Create API Clients From an DSL}
12
+ spec.summary = %q{Create API Clients From an DSL}
13
+ spec.homepage = "http://github.com/cyu/diesel"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake", "~> 10.1"
23
+
24
+ spec.add_dependency "httparty", "~> 0.12"
25
+ end
@@ -0,0 +1,13 @@
1
+ require 'diesel'
2
+ require 'logger'
3
+ PivotalTracker = Diesel.load_api 'apis/pivotal_tracker'
4
+ api = PivotalTracker.new
5
+ api.logger = Logger.new(STDOUT)
6
+ api.logger.level = Logger::DEBUG
7
+ api.api_token = YOUR_API_TOKEN
8
+ p api.create_story(
9
+ project_id: YOUR_PROJECT_ID,
10
+ story: {
11
+ name: 'Testing Pivotal API',
12
+ :story_type => :chore
13
+ })
@@ -0,0 +1,8 @@
1
+ require 'diesel'
2
+ require 'logger'
3
+
4
+ Slack = Diesel.load_api 'apis/slack'
5
+ api = Slack.new
6
+ api.logger = Logger.new STDOUT
7
+ api.api_token = YOUR_API_TOKEN
8
+ puts api.send_message(payload: {text: 'testing', channel: '#developers', username: 'calvin'})
@@ -0,0 +1,92 @@
1
+ require 'httparty'
2
+
3
+ module Diesel
4
+ module Action
5
+ class HTTP
6
+ attr_accessor :filters, :request_method
7
+
8
+ def filters
9
+ @filters ||= []
10
+ end
11
+
12
+ def perform(context)
13
+ req = Request.new(request_method, context.endpoint_url)
14
+ [context.api.class.authenticator].concat(filters).each do |filter|
15
+ filter.apply_filter(req, context)
16
+ end
17
+
18
+ if context.logger && context.logger.debug?
19
+ context.logger.debug("Request Method: #{request_method}")
20
+ context.logger.debug("URL: #{req.url}")
21
+ context.logger.debug("Options: #{req.http_options.inspect}")
22
+ end
23
+
24
+ req.perform
25
+ end
26
+
27
+ class Request
28
+ attr_reader :method, :headers, :query
29
+ attr_accessor :url, :body
30
+
31
+ def initialize(method, url)
32
+ @method = method
33
+ @url = url
34
+ @headers = {}
35
+ @query = {}
36
+ end
37
+
38
+ def perform
39
+ HTTParty.send(method, url, http_options)
40
+ end
41
+
42
+ def http_options
43
+ {headers: headers, query: query, body: body}
44
+ end
45
+
46
+ def add_query_parameter(name, value)
47
+ @url = if url.index('?')
48
+ "#{url}&#{URI.escape(name)}=#{URI.escape(value)}"
49
+ else
50
+ "#{url}?#{URI.escape(name)}=#{URI.escape(value)}"
51
+ end
52
+ end
53
+ end
54
+
55
+ class ParameterFilter
56
+ attr_reader :parameter
57
+
58
+ def initialize(parameter)
59
+ @parameter = parameter
60
+ end
61
+
62
+ def apply_filter(request, context)
63
+ v = find_parameter_value(context)
64
+ case parameter.param_type
65
+ when :query
66
+ request.query[parameter.name] = v
67
+ when :path
68
+ request.url = request.url.gsub(path_regex, v.to_s)
69
+ when :body
70
+ request.body = v
71
+ when :header
72
+ request.headers[parameter.name] = v
73
+ end
74
+ end
75
+
76
+ def find_parameter_value(context)
77
+ if parameter.complex?
78
+ model = context.api.class.models[parameter.data_type]
79
+ model.build(parameter, context)
80
+ else
81
+ parameter.value || context.get_attribute(parameter.name)
82
+ end
83
+ end
84
+
85
+ protected
86
+ def path_regex
87
+ Regexp.new(Regexp.quote("{#{parameter.name}}"))
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
data/lib/diesel/api.rb ADDED
@@ -0,0 +1,33 @@
1
+ module Diesel
2
+ class API
3
+ attr_accessor :logger
4
+
5
+ class << self
6
+ def base_path; @base_path; end
7
+ def base_path=(base_path)
8
+ @base_path = base_path
9
+ end
10
+
11
+ def endpoints; @endpoints ||= []; end
12
+ def endpoints=(endpoints)
13
+ @endpoints = endpoints
14
+ end
15
+
16
+ def authenticator; @authenticator; end
17
+ def authenticator=(auth)
18
+ @authenticator = auth
19
+ @authenticator.activate(self)
20
+ end
21
+
22
+ def models; @models ||= {}; end
23
+ def models=(models)
24
+ @models = models
25
+ end
26
+ end
27
+
28
+ protected
29
+ def execute(endpoint, options)
30
+ RequestContext.new(self, endpoint, options).perform
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,68 @@
1
+ require 'diesel/action/http'
2
+ require 'diesel/complex_type_builder'
3
+ require 'diesel/container_builder'
4
+
5
+ module Diesel
6
+ class APIBuilder
7
+ attr_reader :profile
8
+
9
+ def initialize(profile)
10
+ @profile = profile
11
+ end
12
+
13
+ def build
14
+ create_api_class.tap do |klass|
15
+ klass.base_path = profile.base_path
16
+ unless profile.authorizations.empty?
17
+ klass.authenticator = Diesel::Authenticator.build(profile.authorizations)
18
+ end
19
+ build_endpoints(klass)
20
+ build_models(klass)
21
+ #Diesel.const_set create_class_name(name), klass
22
+ end
23
+ end
24
+
25
+ protected
26
+ def global_filters
27
+ @global_filters ||= profile.parameters.map { |p| Diesel::Action::HTTP::ParameterFilter.new(p) }
28
+ end
29
+
30
+ def create_api_class
31
+ Class.new(Diesel::API)
32
+ end
33
+
34
+ def build_endpoints(klass)
35
+ klass.endpoints = profile.resources.map do |resource|
36
+ resource.operations.map do |operation|
37
+ Diesel::Endpoint.new(resource.path).tap do |endpoint|
38
+ action = Diesel::Action::HTTP.new
39
+ action.request_method = operation.method
40
+ action.filters.concat(global_filters)
41
+ action.filters.concat(operation.parameters.map { |p| Diesel::Action::HTTP::ParameterFilter.new(p) })
42
+ endpoint.action = action
43
+ klass.__send__(:define_method, operation.nickname) do |parameters|
44
+ execute(endpoint, parameters)
45
+ end
46
+ end
47
+ end
48
+ end.flatten.compact
49
+ end
50
+
51
+ def build_models(klass)
52
+ klass.models = profile.models.map do |model|
53
+ if model.is_a? Diesel::Profile::Container
54
+ Diesel::ContainerBuilder.new(model)
55
+ else
56
+ Diesel::ComplexTypeBuilder.new(model)
57
+ end
58
+ end.reduce({}) { |m,b| m[b.model.name] = b; m }
59
+ end
60
+
61
+ def create_class_name(name)
62
+ string = name.to_s.sub(/.*\./, '')
63
+ string = string.sub(/^[a-z\d]*/) { $&.capitalize }
64
+ string.gsub(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{$2.capitalize}" }.gsub('/', '::')
65
+ end
66
+
67
+ end
68
+ end
@@ -0,0 +1,39 @@
1
+ require 'diesel/auth/base'
2
+
3
+ module Diesel
4
+ module Auth
5
+ class APIKey < Base
6
+ attr_accessor :nickname, :pass_as, :name
7
+
8
+ def self.build(authorization)
9
+ auth = new
10
+ auth.name = authorization.name
11
+ auth.nickname = authorization.nickname
12
+ auth.pass_as = authorization.pass_as
13
+ auth
14
+ end
15
+
16
+ def activate(api_class)
17
+ header_name = name
18
+ api_class.__send__(:define_method, "#{nickname}=".to_sym) do |header_value|
19
+ (@_auth_http_headers ||= {})[header_name] = header_value
20
+ end
21
+ api_class.send(:include, InstanceMethods)
22
+ end
23
+
24
+ def apply_filter(request, context)
25
+ if pass_as.to_sym == :header
26
+ request.headers[name] = context.api.auth_http_headers[name]
27
+ elsif pass_as.to_sym == :query_parameter
28
+ request.add_query_parameter(name, context.api.auth_http_headers[name])
29
+ end
30
+ end
31
+
32
+ module InstanceMethods
33
+ def auth_http_headers
34
+ @_auth_http_headers
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,8 @@
1
+ module Diesel
2
+ module Auth
3
+ class Base
4
+ attr_accessor :name
5
+ end
6
+ end
7
+ end
8
+
@@ -0,0 +1,13 @@
1
+ module Diesel
2
+ module Auth
3
+ class OAuth2
4
+ def self.activate(api_class, options)
5
+ api_class.send :attr_accessor, :access_token
6
+ end
7
+
8
+ def self.append_authentication(context, http_options)
9
+ (http_options[:headers] ||= {})['Authorization'] = "Bearer #{context.api.access_token}"
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,35 @@
1
+ module Diesel
2
+ class Authenticator
3
+ attr_reader :strategies
4
+
5
+ def self.build(authorizations)
6
+ strategies = authorizations.map do |auth|
7
+ case auth.type
8
+ when :oauth2
9
+ require 'diesel/auth/oauth2'
10
+ Diesel::Auth::OAuth2.build(auth)
11
+ when :api_key
12
+ require 'diesel/auth/api_key'
13
+ Diesel::Auth::APIKey.build(auth)
14
+ else
15
+ raise "Unsupported authentication: #{auth.type}"
16
+ end
17
+ end
18
+
19
+ new(strategies)
20
+ end
21
+
22
+ def initialize(strategies)
23
+ @strategies = strategies
24
+ end
25
+
26
+ def activate(api_class)
27
+ @strategies.each { |s| s.activate(api_class) }
28
+ end
29
+
30
+ def apply_filter(request, context)
31
+ @strategies.each { |s| s.apply_filter(request, context) }
32
+ end
33
+ end
34
+ end
35
+
@@ -0,0 +1,24 @@
1
+ require 'multi_json'
2
+ require 'diesel/model_builder'
3
+
4
+ module Diesel
5
+ class ComplexTypeBuilder < ModelBuilder
6
+ def build(parameter, context)
7
+ h = context.get_attribute(parameter.name)
8
+ unless h
9
+ if parameter.required?
10
+ raise "Missing required parameter #{parameter.name}"
11
+ else
12
+ return nil
13
+ end
14
+ end
15
+ if model.required.detect { |r| h[r].nil? }
16
+ raise "Missing required value #{r} in #{parameter.name}"
17
+ end
18
+ json = model.properties.reduce({}) do |m, property|
19
+ m[property.name] = h[property.name] if h[property.name]; m
20
+ end
21
+ MultiJson.dump(json)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,6 @@
1
+ require 'diesel/model_builder'
2
+
3
+ module Diesel
4
+ class ContainerBuilder < ModelBuilder
5
+ end
6
+ end
data/lib/diesel/dsl.rb ADDED
@@ -0,0 +1,239 @@
1
+ require 'diesel/profile'
2
+ require 'diesel/endpoint'
3
+ require 'diesel/request_context'
4
+
5
+ module Diesel
6
+ module DSL
7
+ def self.load_profile(name, filename)
8
+ loader = DSL::Loader.new(name)
9
+ loader.instance_eval File.read(filename), filename
10
+ loader.profile
11
+ end
12
+
13
+ class LoaderBase
14
+ attr_reader :object
15
+
16
+ class << self
17
+ def attribute(name)
18
+ define_method(name) do |val|
19
+ @object.__send__("#{name}=".to_sym, val)
20
+ end
21
+ end
22
+ end
23
+
24
+ attribute :description
25
+ end
26
+
27
+ module Parameters
28
+ def header(*args, &block)
29
+ ParameterLoader.load_parameters(@object, :header, *args, &block)
30
+ end
31
+
32
+ def body(*args, &block)
33
+ ParameterLoader.load_parameters(@object, :body, *args, &block)
34
+ end
35
+
36
+ def parameter(*args, &block)
37
+ ParameterLoader.load_parameters(@object, nil, *args, &block)
38
+ end
39
+ end
40
+
41
+ #class PartLoader
42
+ # attr_reader :part
43
+
44
+ # def initialize(*args)
45
+ # @part = Part.new(*args)
46
+ # end
47
+
48
+ # def header name, value
49
+ # @part.set_header(name, value)
50
+ # end
51
+
52
+ # def body value
53
+ # @part.body = value
54
+ # end
55
+ #end
56
+
57
+ #class ActionLoader < PartLoader
58
+ # attr_reader :action
59
+
60
+ # def initialize(endpoint, method, options)
61
+ # @part = @action = Action.new(endpoint, method, options)
62
+ # end
63
+
64
+ # def multipart(options = {}, &block)
65
+ # loader = MultipartLoader.new(options)
66
+ # loader.instance_eval(&block)
67
+ # action.multipart = loader.multipart
68
+ # end
69
+ #end
70
+
71
+ #class MultipartLoader
72
+ # attr_reader :multipart
73
+
74
+ # def initialize(options)
75
+ # @multipart = Multipart.new(options)
76
+ # end
77
+
78
+ # def part(*args, &block)
79
+ # loader = PartLoader.new(*args)
80
+ # loader.instance_eval(&block) if block
81
+ # multipart.add_part(loader.part)
82
+ # end
83
+ #end
84
+
85
+ class AuthorizationLoader < LoaderBase
86
+ attribute :name
87
+ attribute :nickname
88
+ attribute :pass_as
89
+ attribute :type
90
+
91
+ def initialize
92
+ @object = Diesel::Profile::Authorization.new
93
+ end
94
+ end
95
+
96
+ class PropertyLoader < LoaderBase
97
+ attribute :name
98
+
99
+ def initialize
100
+ @object = Diesel::Profile::Property.new
101
+ end
102
+
103
+ def enum(*values)
104
+ @object.enum = values
105
+ end
106
+ end
107
+
108
+ class ComplexTypeLoader < LoaderBase
109
+ attribute :name
110
+
111
+ def initialize
112
+ @object = Diesel::Profile::ComplexType.new
113
+ end
114
+
115
+ def required(*required_properties)
116
+ @object.required = required_properties
117
+ end
118
+
119
+ def property(name, &block)
120
+ l = PropertyLoader.new
121
+ l.name(name)
122
+ l.instance_eval(&block) if block
123
+ @object.properties << l.object
124
+ end
125
+ end
126
+
127
+ class OperationLoader < LoaderBase
128
+ include Parameters
129
+
130
+ attribute :nickname
131
+ attribute :reference_url
132
+ attribute :method
133
+
134
+ def initialize
135
+ @object = Diesel::Profile::Operation.new
136
+ end
137
+ end
138
+
139
+ class ResourceLoader < LoaderBase
140
+ def initialize(path)
141
+ @object = Diesel::Profile::Resource.new(path)
142
+ end
143
+
144
+ def operation(nickname, &block)
145
+ l = OperationLoader.new
146
+ l.nickname(nickname)
147
+ l.instance_eval(&block)
148
+ @object.operations << l.object
149
+ end
150
+ end
151
+
152
+ class ParameterLoader < LoaderBase
153
+ attribute :param_type
154
+ attribute :name
155
+ attribute :value
156
+ attribute :required
157
+ attribute :data_type
158
+
159
+ def initialize
160
+ @object = Diesel::Profile::Parameter.new
161
+ end
162
+
163
+ def required(required = true)
164
+ @object.required = required
165
+ end
166
+
167
+ def self.load_parameters(target, param_type, *args, &block)
168
+ if args.first.is_a?(::Hash)
169
+ args.shift.each_pair do |name, value|
170
+ l = new
171
+ l.param_type(param_type)
172
+ l.name(name)
173
+ l.value(value)
174
+ target.parameters << l.object
175
+ end
176
+ else
177
+ l = new
178
+ l.param_type(param_type)
179
+ l.name(args.first)
180
+ l.instance_eval(&block) if block
181
+ target.parameters << l.object
182
+ end
183
+ end
184
+ end
185
+
186
+ class APILoader < LoaderBase
187
+ include Parameters
188
+
189
+ attribute :api_version
190
+ attribute :base_path
191
+
192
+ def initialize(api_profile)
193
+ @object = api_profile
194
+ end
195
+
196
+ def resource(path, &block)
197
+ l = ResourceLoader.new(path)
198
+ l.instance_eval(&block)
199
+ @object.resources << l.object
200
+ end
201
+
202
+ def complex_type(name, &block)
203
+ l = ComplexTypeLoader.new
204
+ l.name(name)
205
+ l.instance_eval(&block)
206
+ @object.models << l.object
207
+ end
208
+
209
+ def container_type(name, &block)
210
+ l = ContainerTypeLoader.new
211
+ l.name(name)
212
+ l.instance_eval(&block)
213
+ @object.models << l.object
214
+ end
215
+
216
+ def api_key(name, &block)
217
+ l = AuthorizationLoader.new
218
+ l.name(name)
219
+ l.type(:api_key)
220
+ l.instance_eval(&block)
221
+ @object.authorizations << l.object
222
+ end
223
+ end
224
+
225
+ class Loader
226
+ attr_reader :name
227
+ attr_reader :profile
228
+
229
+ def initialize(name)
230
+ @name = name
231
+ end
232
+
233
+ def apis(&block)
234
+ @profile = Profile::API.new(name)
235
+ APILoader.new(@profile).instance_eval(&block)
236
+ end
237
+ end
238
+ end
239
+ end
@@ -0,0 +1,43 @@
1
+ module Diesel
2
+ class Endpoint
3
+ attr_reader :path
4
+ attr_accessor :action
5
+
6
+ def initialize(path)
7
+ @path = path
8
+ end
9
+
10
+ def perform(context)
11
+ raise 'action required for endpoint' unless action
12
+
13
+ assert_attributes(context)
14
+
15
+ #unless action.accept? context
16
+ # raise 'endpoint does not support this context'
17
+ #end
18
+
19
+ action.perform(context)
20
+ end
21
+
22
+ private
23
+ def assert_attributes(context)
24
+ #if attributes.nil?
25
+ # unless context.attributes.empty?
26
+ # raise ArgumentError, "invalid attributes #{atts.keys.join(', ')}"
27
+ # end
28
+ # return
29
+ #end
30
+
31
+ #invalid_atts = context.attributes.keys.map!(&:intern) - attributes.keys
32
+ #unless invalid_atts.empty?
33
+ # raise ArgumentError, "invalid attributes #{invalid_atts.join(', ')}"
34
+ #end
35
+
36
+ #attributes.each do |attr_name, options|
37
+ # if options[:required] and context.attributes[attr_name].nil?
38
+ # raise ArgumentError, "require attribute: #{attr_name}"
39
+ # end
40
+ #end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,9 @@
1
+ module Diesel
2
+ class ModelBuilder
3
+ attr_reader :model
4
+
5
+ def initialize(model)
6
+ @model = model
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,112 @@
1
+ require 'diesel/api'
2
+ require 'diesel/authenticator'
3
+
4
+ module Diesel
5
+ module Profile
6
+ class Base
7
+ attr_accessor :description
8
+ end
9
+
10
+ class Authorization < Base
11
+ attr_accessor :name, :nickname, :pass_as, :type
12
+ end
13
+
14
+ class Property < Base
15
+ attr_accessor :name, :enum
16
+ end
17
+
18
+ class Model < Base
19
+ attr_accessor :name
20
+ end
21
+
22
+ class ComplexType < Model
23
+ attr_accessor :required, :properties
24
+
25
+ def required
26
+ @required ||= []
27
+ end
28
+
29
+ def properties
30
+ @properties ||= []
31
+ end
32
+ end
33
+
34
+ class Container < Model
35
+ attr_accessor :type
36
+ end
37
+
38
+ class Parameter < Base
39
+ PRIMITIVE_TYPES = [
40
+ :integer,
41
+ :long,
42
+ :float,
43
+ :double,
44
+ :string,
45
+ :byte,
46
+ :boolean,
47
+ :date,
48
+ :date_time
49
+ ]
50
+
51
+ attr_accessor :param_type, :name, :value, :data_type, :required
52
+
53
+ def complex?
54
+ !data_type.nil? && !primitive?
55
+ end
56
+
57
+ def primitive?
58
+ PRIMITIVE_TYPES.include?(data_type)
59
+ end
60
+
61
+ def required?
62
+ !!required
63
+ end
64
+ end
65
+
66
+ class Operation < Base
67
+ attr_accessor :nickname, :reference_url, :method, :parameters
68
+
69
+ def parameters
70
+ @parameters ||= []
71
+ end
72
+ end
73
+
74
+ class Resource < Base
75
+ attr_accessor :path, :operations
76
+
77
+ def initialize(path)
78
+ @path = path
79
+ end
80
+
81
+ def operations
82
+ @operations ||= []
83
+ end
84
+ end
85
+
86
+ class API < Base
87
+ attr_reader :name
88
+ attr_accessor :api_version, :base_path, :auth_type, :auth_options, :resources, :parameters, :authorizations
89
+
90
+ def initialize(name)
91
+ @name = name
92
+ end
93
+
94
+ def parameters
95
+ @parameters ||= []
96
+ end
97
+
98
+ def resources
99
+ @resources ||= []
100
+ end
101
+
102
+ def models
103
+ @models ||= []
104
+ end
105
+
106
+ def authorizations
107
+ @authorizations ||= []
108
+ end
109
+ end
110
+
111
+ end
112
+ end
@@ -0,0 +1,30 @@
1
+ module Diesel
2
+ class RequestContext
3
+ attr_reader :api, :endpoint, :attributes
4
+
5
+ def initialize(api, endpoint, attributes)
6
+ @api, @endpoint, @attributes = api, endpoint, attributes
7
+ end
8
+
9
+ def logger
10
+ api.logger
11
+ end
12
+
13
+ def get_attribute(name)
14
+ name = name.to_sym
15
+ attributes[name]
16
+ end
17
+
18
+ def endpoint_url
19
+ url = api.class.base_path.dup
20
+ url << "/" unless url =~ /\/$/
21
+ endpoint_path = endpoint.path
22
+ url << (endpoint_path =~ /^\// ? endpoint_path[1..-1] : endpoint_path)
23
+ url
24
+ end
25
+
26
+ def perform
27
+ endpoint.perform(self)
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,3 @@
1
+ module Diesel
2
+ VERSION = "0.0.1"
3
+ end
data/lib/diesel.rb ADDED
@@ -0,0 +1,10 @@
1
+ require "diesel/version"
2
+ require 'diesel/dsl'
3
+ require 'diesel/api_builder'
4
+
5
+ module Diesel
6
+ def self.load_api(name)
7
+ profile = DSL.load_profile(name, "#{name}.rb")
8
+ Diesel::APIBuilder.new(profile).build
9
+ end
10
+ end
metadata ADDED
@@ -0,0 +1,112 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: diesel-api-dsl
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Calvin Yu
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-03-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '10.1'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '10.1'
41
+ - !ruby/object:Gem::Dependency
42
+ name: httparty
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '0.12'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '0.12'
55
+ description: Create API Clients From an DSL
56
+ email:
57
+ - me@sourcebender.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - .gitignore
63
+ - Gemfile
64
+ - LICENSE
65
+ - README.md
66
+ - Rakefile
67
+ - apis/pivotal_tracker.rb
68
+ - apis/slack.rb
69
+ - diesel.gemspec
70
+ - examples/create_pivotal_task.rb
71
+ - examples/post_slack_message.rb
72
+ - lib/diesel.rb
73
+ - lib/diesel/action/http.rb
74
+ - lib/diesel/api.rb
75
+ - lib/diesel/api_builder.rb
76
+ - lib/diesel/auth/api_key.rb
77
+ - lib/diesel/auth/base.rb
78
+ - lib/diesel/auth/oauth2.rb
79
+ - lib/diesel/authenticator.rb
80
+ - lib/diesel/complex_type_builder.rb
81
+ - lib/diesel/container_builder.rb
82
+ - lib/diesel/dsl.rb
83
+ - lib/diesel/endpoint.rb
84
+ - lib/diesel/model_builder.rb
85
+ - lib/diesel/profile.rb
86
+ - lib/diesel/request_context.rb
87
+ - lib/diesel/version.rb
88
+ homepage: http://github.com/cyu/diesel
89
+ licenses:
90
+ - MIT
91
+ metadata: {}
92
+ post_install_message:
93
+ rdoc_options: []
94
+ require_paths:
95
+ - lib
96
+ required_ruby_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ! '>='
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ required_rubygems_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ! '>='
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ requirements: []
107
+ rubyforge_project:
108
+ rubygems_version: 2.2.2
109
+ signing_key:
110
+ specification_version: 4
111
+ summary: Create API Clients From an DSL
112
+ test_files: []