gutsy 0.1.0

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: da3a90cbf88bebcc6de078248e83b515338e5be3
4
+ data.tar.gz: d9fdb9b74896ff504fe4e45f529fda7ee97f3a8c
5
+ SHA512:
6
+ metadata.gz: 3386699eea4d6be07d403c3fedee8efdc5024bf369e3a926af9d7a111aa02bb5ba87e72d686580258656067e8081513aa7167508d1aa8680eaa74536a7bb00fd
7
+ data.tar.gz: 036a6f184c812021f132f8519fb03b362c7ede9a747022e5e808e5938efca651d4cc3c2d394b2a1c874c535336342cfe0adb68c9090ed3ac7727f71456935184
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.log
2
+ output/*
3
+ *.gem
4
+
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.0
5
+ before_install: gem install bundler -v 1.12.5
@@ -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 max.fierke@iorahealth.com. 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,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in example_app_client.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,62 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ gutsy (0.1.0)
5
+ activesupport (>= 3.2)
6
+ heroics (~> 0.0.17)
7
+ json_schema (~> 0.13)
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ activesupport (4.1.11)
13
+ i18n (~> 0.6, >= 0.6.9)
14
+ json (~> 1.7, >= 1.7.7)
15
+ minitest (~> 5.1)
16
+ thread_safe (~> 0.1)
17
+ tzinfo (~> 1.1)
18
+ diff-lcs (1.2.5)
19
+ erubis (2.7.0)
20
+ excon (0.51.0)
21
+ heroics (0.0.17)
22
+ erubis (~> 2.0)
23
+ excon
24
+ moneta
25
+ multi_json (>= 1.9.2)
26
+ netrc
27
+ i18n (0.7.0)
28
+ json (1.8.3)
29
+ json_schema (0.13.0)
30
+ minitest (5.8.3)
31
+ moneta (0.8.0)
32
+ multi_json (1.12.1)
33
+ netrc (0.11.0)
34
+ rake (10.5.0)
35
+ rspec (3.5.0)
36
+ rspec-core (~> 3.5.0)
37
+ rspec-expectations (~> 3.5.0)
38
+ rspec-mocks (~> 3.5.0)
39
+ rspec-core (3.5.2)
40
+ rspec-support (~> 3.5.0)
41
+ rspec-expectations (3.5.0)
42
+ diff-lcs (>= 1.2.0, < 2.0)
43
+ rspec-support (~> 3.5.0)
44
+ rspec-mocks (3.5.0)
45
+ diff-lcs (>= 1.2.0, < 2.0)
46
+ rspec-support (~> 3.5.0)
47
+ rspec-support (3.5.0)
48
+ thread_safe (0.3.5)
49
+ tzinfo (1.2.2)
50
+ thread_safe (~> 0.1)
51
+
52
+ PLATFORMS
53
+ ruby
54
+
55
+ DEPENDENCIES
56
+ bundler (~> 1.12)
57
+ gutsy!
58
+ rake (~> 10.0)
59
+ rspec (~> 3.0)
60
+
61
+ BUNDLED WITH
62
+ 1.12.5
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Iora Health
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,56 @@
1
+ # Gutsy
2
+
3
+ <center>
4
+ <pre>
5
+
6
+ ╓▄╖,
7
+ ,▄▓▓▒░░ ╓▄▓▓█▓▄µ ╓▄▄▄▒▄╗╦, ╓▓▓▓▓▒░║▓
8
+ ╟▓▓▓▓▄▄▄▌ ▓█▓▓▓▓▄╙▓╦ ╚▓▓▓▓▓█▒▒▒░~ ▐▓▓▓▓▓▓▓▓▓
9
+ ╟▓▓▓▓██▓Å▒ ▐▓▓▓▓╣▓▓▌▐█▒▒▓█░╬░░░-⌐ `. ▓▓▓▓▓▓▓▓▓
10
+ ▀Φ▓▓▓▓▓Θ ▓▓▓▓▓▓▓▓▓▒╬▓▓▓▌δ░░░░"░ W,▓▓▓Θ `█▓▒▓▓▓▀▀
11
+ └▓▀▓ ╣▓▓▓▓▓▓▓▓▓▓▓▓▀▓▄╬░ └Γ▀▓▓█▄ ▓Γ
12
+ ▓ ▓▓▓▓▓▓▓▓▓▓▓▄▓▓▓▀▓▓▌▌ └▌╙▓▓▓▄ ▓▀
13
+ ╙▓▄▓▓▓▓▓▓▓▓▓▀▓▓▓▓▓▌╬▒ ^▓▓▓▄▓█
14
+ ▓▓ █ ╠▓▓▓▌▓▓▓▓▓▓▓▌Ü ▀▓▓▓
15
+ █▓▓▒▓▓▓▓▓▓▓╣Ö`^``` , Γ
16
+ █▓▓▓▓▓▓▓▓▌▓δ^^^^` ░░. ,,
17
+ ╙▓▓▓▓▓▓▓▓▓▓▌▒▒▒▄░╣▄▄▒▒▒`
18
+ ,▓▓▓▓▓▓▓██▓▓▓▓▓▓▓▓▓▓▓▓▌░╤
19
+ ▄ ,▄ ▄▓▓▓▓▓▓▓▓╣╬δ▒▓▓▓▄▓▓▓▓▓▓▓▓╦
20
+ ╚╣▓▓▓▓▄ ╒▓▓▓▓▓▓▓▓▓▓Ñ░-▓▓▓▓▓▒ ▀█▓▓▓▓▌▄
21
+ ▀▀▓▓▓▓▌ ╙▓▓▓▓▓▀╒▓▓╡ ╫▓▒╫▓▓Θ ▀▓▓▓▓▓╛ ▄▄▄▄▄▓▓▓▓
22
+ ▀▓▓▓▌ ╙▀╫▓▄▄╦▓▓▓▒╦╥▓▓╣▓▓▓▒ ,▄▓▓▓y ▄▓▓▄▓▓█▀ΓΓ▀ΓΓ.
23
+ ▓▓▓▌ ▄▓▓▓▓▓▓▓▓▓▓▒▓▓╠╫▓▌░ ╔▓▓▓▓▌▄ ▓▓▓▓▓▓█
24
+ ▓▌▌ █▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▀` ╓▓▓▓▓▓▌δ ╙▓▓▓▓▀
25
+ ▓─ ▀▓▓▓▓▓▓▓▓▓▓▓▓▓▌ ,▓▓▓▓▓▓▒ ▐▓▓█
26
+ ▓ ╙▓▓▓▓▓▓█╢▓▓▓▓▓▌ ▓▓▓▓▓▓▀` ▓▌
27
+ ▌δ █▓▓▓▌░ ╙▓▓▀▀╘▓▓▓▓▓▓` ╒▓▌
28
+ ▐▓ ▌╓p╦▄▄▄▄,▓▓▓▓▓▌╦░▓█ ▓▓▓▓Σ ▌▓µ
29
+ ▓▌▄▓▓▓▄╠▓╣▌▄▓▓▓▓▓▓▓Θ╫▌ ^▓▓▓▌╕ ╬ ▓
30
+ ▀▀▓▓▓▓▓▄▌δ▓▓▓▓█▓▓Θ⌐╟▌ ▓▓▓▌░ ╒Γ▐▓∩
31
+ .▀█▓▓▓▄▓▓▓█ ▓▓░ ▒⌐ ▓▓▓▌╗▄╗╗QQQQ▒▒▓▌▓▓▌
32
+ Γ▓▓▒` ▓▓░⌐▓ █▓▓▓▓▓▓▓▓███▀▀▀▀▀
33
+ ▓▌ ▓▓▌╦█ ╙▀
34
+ ╫Σ ƒ▓ΘΓ▓▄
35
+ ╫▒ ▄█░░4
36
+ ▐▌╫▓░▄╝
37
+ ▐▓▓▓▓▀
38
+ ▐▓▌▌
39
+ ^▀Γ
40
+ </pre>
41
+ </center>
42
+
43
+ Gutsy generates RubyGem wrappers and documentation for [heroics](https://github.com/interagent/heroics)-generated API clients from JSON schema. It's the Ruby side of the coin to [`braise`](https://github.com/IoraHealth/braise).
44
+
45
+ ## Usage
46
+
47
+ ```bash
48
+ $ gem install gutsy
49
+ $ gutsy generate AppName /path/to/schema.json /path/for/output/
50
+ ```
51
+
52
+ Check out your generated API gem! You'll probably want to edit the `gemspec`, `README.md`, and `LICENSE.txt` to better fit your needs.
53
+
54
+ ## License
55
+
56
+ See [LICENSE.txt](LICENSE.txt)
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "gutsy"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/gutsy ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "gutsy"
5
+
6
+ Gutsy.initialize!
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/gutsy.gemspec ADDED
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'gutsy/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "gutsy"
8
+ spec.version = Gutsy::VERSION
9
+ spec.authors = ["Iora Health"]
10
+ spec.email = ["rubygems@iorahealth.com"]
11
+
12
+ spec.summary = "Generates heroics-powered API client gem wrappers from JSON Schema"
13
+ spec.description = spec.summary
14
+ spec.homepage = "https://github.com/IoraHealth/gutsy"
15
+ spec.license = "MIT"
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.require_paths = ["lib"]
18
+
19
+ spec.executables << 'gutsy'
20
+
21
+ spec.add_runtime_dependency "heroics", "~> 0.0.17"
22
+ spec.add_runtime_dependency "json_schema", "~> 0.13"
23
+ spec.add_runtime_dependency "activesupport", ">= 3.2"
24
+
25
+ spec.add_development_dependency "bundler", "~> 1.12"
26
+ spec.add_development_dependency "rake", "~> 10.0"
27
+ spec.add_development_dependency "rspec", "~> 3.0"
28
+ end
data/lib/gutsy/cli.rb ADDED
@@ -0,0 +1,7 @@
1
+ module Gutsy
2
+ module Cli
3
+ # Perhaps one day there will be code here
4
+ end
5
+ end
6
+
7
+ require 'gutsy/generator'
@@ -0,0 +1,202 @@
1
+ module Gutsy
2
+ module Cli
3
+ class Generator
4
+ extend Forwardable
5
+
6
+ class State
7
+ attr_reader :app_name
8
+ attr_accessor :resources
9
+
10
+ def initialize(app_name, resources=[])
11
+ @app_name = app_name
12
+ @resources = resources
13
+ end
14
+
15
+ def gem_name
16
+ @gem_name_snake ||= "#{app_name.underscore}_client"
17
+ end
18
+
19
+ def gem_name_snake
20
+ gem_name
21
+ end
22
+
23
+ def gem_name_pascal
24
+ @gem_name_pascal ||= gem_name.camelize(:upper)
25
+ end
26
+
27
+ def copyright_year
28
+ @copyright_year ||= Time.now.year
29
+ end
30
+
31
+ def copyright_owner
32
+ @copyright_owner ||= "YOUR_NAME_HERE"
33
+ end
34
+
35
+ def twine
36
+ binding
37
+ end
38
+ end
39
+
40
+ class ResourceState
41
+ attr_reader :resource_name, :gem_name_pascal
42
+
43
+ def initialize(resource_name, gem_name_pascal)
44
+ @resource_name = resource_name
45
+ @gem_name_pascal = gem_name_pascal
46
+ end
47
+
48
+ def twine
49
+ binding
50
+ end
51
+ end
52
+
53
+ attr_reader :app_name
54
+
55
+ def initialize(app_name, schema_path, output_path)
56
+ @state = State.new(app_name)
57
+ @schema_path = schema_path
58
+ @output_path = output_path
59
+ end
60
+
61
+ def generate!
62
+ create_output_dir
63
+
64
+ schema = load_and_validate_schema!
65
+
66
+ state.resources = map_schema_to_resources(schema)
67
+
68
+ scaffold_gem
69
+
70
+ generate_heroics_client
71
+
72
+ puts "Generated client gem can be found in... #{output_path}"
73
+ end
74
+
75
+ private
76
+
77
+ attr_reader :state, :schema_path, :output_path
78
+ attr_accessor :schema
79
+ def_delegators :state, :app_name, :gem_name_snake, :gem_name_pascal
80
+
81
+ def create_output_dir
82
+ print "Creating #{output_path}..."
83
+ Dir.mkdir(output_path, 0755) unless Dir.exist?(output_path)
84
+ puts "OK"
85
+ end
86
+
87
+ def load_and_validate_schema!
88
+ print "Validating schema against draft-04 JSON Schema..."
89
+ draft04_uri = URI.parse("http://json-schema.org/draft-04/hyper-schema")
90
+ draft04 = JsonSchema.parse!(JSON.parse(draft04_uri.read))
91
+
92
+ schema_json = JSON.parse(File.read(schema_path))
93
+
94
+ schema = JsonSchema.parse!(schema_json)
95
+ schema.expand_references!
96
+
97
+ draft04.validate!(schema)
98
+ puts "OK"
99
+
100
+ schema
101
+ end
102
+
103
+ def map_schema_to_resources(schema)
104
+ resources = Hash[schema.definitions.map do |key, resource|
105
+ links = Hash[resource.links.map do |link|
106
+ link.schema.expand_references! if link.schema
107
+ properties = link.schema.try(:properties) || {}
108
+ [link.title.downcase.to_sym, OpenStruct.new(properties: properties)]
109
+ end]
110
+ [key.to_sym, OpenStruct.new(title: key.camelize, links: links)]
111
+ end]
112
+ resources
113
+ end
114
+
115
+ def scaffold_gem
116
+ print "Creating gem directory structure..."
117
+ template_dirs.each do |dir|
118
+ dir = dir.gsub('app_client', gem_name_snake)
119
+ dir_path = File.join(output_path, dir)
120
+ Dir.mkdir(dir_path, 0755) unless Dir.exist?(dir_path)
121
+ end
122
+ puts "OK"
123
+
124
+ [
125
+ ".gitignore",
126
+ "Gemfile",
127
+ "LICENSE.txt",
128
+ "Rakefile",
129
+ "README.md",
130
+ ].each do |file|
131
+ copy_file file
132
+ end
133
+
134
+ copy_file "app_client.gemspec",
135
+ as: "#{gem_name_snake}.gemspec"
136
+
137
+ copy_file "lib/app_client.rb",
138
+ as: "lib/#{gem_name_snake}.rb"
139
+
140
+ [
141
+ "version.rb",
142
+ "v1/adapter.rb"
143
+ ].each do |file|
144
+ copy_file "lib/app_client/#{file}",
145
+ as: "lib/#{gem_name_snake}/#{file}"
146
+ end
147
+
148
+ state.resources.each do |key, resource|
149
+ copy_file "lib/app_client/v1/resource.rb",
150
+ as: "lib/#{gem_name_snake}/v1/#{key.to_s.underscore}.rb",
151
+ binding: ResourceState.new(key.to_s, gem_name_pascal).twine
152
+ end
153
+ end
154
+
155
+ def generate_heroics_client
156
+ print "Generating Heroics client for JSON Schema..."
157
+ unless system "heroics-generate \
158
+ #{gem_name_pascal}::V1::Adapters::Http \
159
+ #{schema_path} \
160
+ http://#{app_name.downcase}/api/v1 > \
161
+ #{output_path}/lib/#{gem_name_snake}/v1/adapters/http.rb"
162
+ puts "FAIL"
163
+ puts "Please see stacktrace or heroics errors"
164
+ end
165
+ puts "OK"
166
+ end
167
+
168
+ # Derived from Methadone::Cli (https://github.com/davetron5000/methadone/blob/935444f9deb81100a33ec3234effbeb65acbc080/lib/methadone/cli.rb)
169
+ def copy_file(relative_path,options = {})
170
+ relative_path = File.join(relative_path.split(/\//))
171
+
172
+ template_path = File.join(template_dir, "#{relative_path}.erb")
173
+ template = ERB.new(File.read(template_path), nil, '-')
174
+
175
+ output_relative_path ||= options[:as] || relative_path
176
+
177
+ file_output_path = File.join(output_path, output_relative_path)
178
+
179
+ print "Copying #{file_output_path}..."
180
+
181
+ erb_binding = options[:binding] || state.twine
182
+
183
+ File.open(file_output_path, 'w') do |file|
184
+ file.puts template.result(erb_binding)
185
+ file.chmod(0755) if options[:executable]
186
+ end
187
+
188
+ print "OK\n"
189
+ end
190
+
191
+ def template_dir
192
+ @template_dir ||= File.join(File.dirname(__FILE__), '..', '..', 'templates', 'app_client')
193
+ end
194
+
195
+ def template_dirs
196
+ Dir["#{template_dir}/**/*"].
197
+ select { |x| File.directory? x }.
198
+ map { |dir| dir.gsub(/^#{template_dir}\//,'') }
199
+ end
200
+ end
201
+ end
202
+ end
@@ -0,0 +1,3 @@
1
+ module Gutsy
2
+ VERSION = "0.1.0"
3
+ end
data/lib/gutsy.rb ADDED
@@ -0,0 +1,72 @@
1
+ require 'active_support'
2
+ require 'active_support/core_ext/object/try'
3
+ require 'active_support/core_ext/string/inflections'
4
+ require 'erb'
5
+ require 'json_schema'
6
+ require 'open-uri'
7
+ require 'gutsy/version'
8
+ require 'gutsy/cli'
9
+
10
+ module Gutsy
11
+ def self.initialize!
12
+ args = ARGV
13
+
14
+ command = args[0]
15
+
16
+ case command
17
+ when "generate"
18
+ unless args.length == 4
19
+ puts <<-TEXT
20
+ Error: Not enough arguments for command 'generate'
21
+
22
+ Usage: gutsy generate [app_name] [schema_path] [output_path]
23
+
24
+ DESCRIPTION
25
+ Generates a gem scaffold and resource API clients on top of a heroics-generated client.
26
+
27
+ ARGUMENTS
28
+ [app_name] - CamelCased name of your application
29
+ [schema_path] - Path to your JSON Schema file
30
+ [output_path] - Path to output generated API client gem.
31
+ (Will be created if it doesn't exist)
32
+ TEXT
33
+ exit 1
34
+ end
35
+ app_name = args[1]
36
+ schema_path = File.expand_path(args[2])
37
+ output_path = File.expand_path(args[3])
38
+
39
+ generator = Gutsy::Cli::Generator.new(app_name, schema_path, output_path)
40
+
41
+ begin
42
+ generator.generate!
43
+ rescue => e
44
+ puts "FAIL"
45
+ puts e.message
46
+ puts e.backtrace.join("\n")
47
+ exit 1
48
+ end
49
+
50
+ exit 0
51
+ when "version"
52
+ puts "Gutsy version #{Gutsy::VERSION}"
53
+ exit 0
54
+ else
55
+ puts <<-TEXT
56
+ Usage: gutsy [command] [options]
57
+
58
+ DESCRIPTION
59
+ Generates gem wrappers around heroics-generated API clients
60
+ built with JSON Schema. (Enough layers of generation for ya?)
61
+
62
+ COMMANDS
63
+ generate scaffolds out an API client
64
+ version returns the gutsy version
65
+ help displays this message
66
+
67
+ Shouts out Mr. Gutsy. Keep on plugging in the Wasteland.
68
+ TEXT
69
+ exit 0
70
+ end
71
+ end
72
+ end
File without changes
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in <%= gem_name_snake %>.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) <%= copyright_year %> <%= copyright_owner %>
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.
@@ -0,0 +1,91 @@
1
+ # <%= app_name %> Ruby Client
2
+
3
+ <%= app_name %> is an application that does things. This gem provides a wrapper around an HTTP Client generated from <%= app_name %>'s JSON Schema using [heroics](https://github.com/interagent/heroics).
4
+
5
+
6
+ ## Usage
7
+
8
+ Add this to your Gemfile
9
+
10
+ ```
11
+ gem '<%= gem_name_snake %>' #, git: 'git@github.com:USER/REPO.git', tag: 'v0.1.0'
12
+ ```
13
+
14
+ ### Rails Setup
15
+
16
+ * Add to an initializer (e.g. `config/initializers/<%= gem_name_snake %>.rb`)
17
+
18
+ ```ruby
19
+ <%= gem_name_pascal %>.configure do |config|
20
+ config.base_url = "http://<%= app_name.downcase %>.dev"
21
+ config.api_key = "i-am-api-user"
22
+ config.api_secret = "very-secret-sssshhh"
23
+ # or OAuth access token
24
+ # config.access_token = "SHA-goodness"
25
+ end
26
+ ```
27
+
28
+ ### Example
29
+
30
+ ```
31
+ class SomeResourceLister
32
+ attr_accessor :client
33
+
34
+ def initialize(access_token)
35
+ @client = <%= gem_name_pascal %>::V1::SomeResource.new(access_token: access_token)
36
+ end
37
+
38
+ def call
39
+ response = client.list({ "start_time" => Time.now.iso8601, "end_time" => (Time.now + 7.days).iso8601 })
40
+
41
+ if response.ok?
42
+ response.some_resources
43
+ else
44
+ response.errors
45
+ end
46
+ end
47
+ end
48
+ ```
49
+
50
+ ## API
51
+
52
+ All Client and Resource API methods return an Object mirroring the JSON response of the <%= app_name.capitalize %> API call, with the added method `ok?`, indicating whether the call was a success (`2xx`), or whether the call was invalid (`422`). Other failures will raise an `Excon::Errors::Error` such as an `Excon::Errors::HTTPStatusError`.
53
+
54
+ The methods supported by each Resource API are generated by heroics and are determined by the JSON schema. In most cases, the Resource APIs found in `lib/<%= gem_name_snake %>` just proxy method calls to the API adapter (by default the heroics-generated API adapter).
55
+
56
+ <% resources.each do |key, resource| %>
57
+
58
+ ### <%= resource.title.camelize %>
59
+
60
+ #### `::new(client=nil, access_token: nil, api_key: nil, api_secret: nil, options: {})`
61
+
62
+ Called with no arguments, `client` defaults to a newly created heroics client instance using the authentication details specified in an initializer/with `<%= gem_name_pascal %>.configure`. Passing an optional argument will override the authentication specified in `<%= gem_name_pascal %>.configuration` for the instance.
63
+
64
+ #### Methods
65
+
66
+ <% resource.links.each do |key, link| -%>
67
+ <% if key == :list || key == :create -%>
68
+ ##### `#<%= key %>(body)`
69
+
70
+ * `body` - Hash containing the following properties:
71
+ <% link.properties.each do |key, prop| -%>
72
+ * `<%= key %>` - <%= prop.description %>
73
+ <% end -%>
74
+ <% end -%>
75
+ <% if key == :info || key == :destroy -%>
76
+ ##### `#<%= key %>(id)`
77
+
78
+ * `id` - <%= resource.title.titleize %> identifier
79
+
80
+ <% end -%>
81
+ <% if key == :update -%>
82
+ ##### `#<%= key %>(id, body)`
83
+
84
+ * `id` - <%= resource.title.titleize %> identifier
85
+ * `body` - Hash containing the following properties:
86
+ <% link.properties.each do |key, prop| -%>
87
+ * `<%= key %>` - <%= prop.description %>
88
+ <% end -%>
89
+ <% end -%>
90
+ <% end -%>
91
+ <% end -%>
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+ task :default => :spec
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $LOAD_PATH.push File.expand_path("lib/<%= gem_name_snake %>", __FILE__)
3
+ require "./lib/<%= gem_name_snake %>/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = '<%= gem_name_snake %>'
7
+ s.version = <%= gem_name_pascal %>::VERSION
8
+ s.authors = ["AUTHOR"]
9
+ s.email = "author@localhost"
10
+ s.summary = "Gutsy-generated gem for <%= app_name %> client"
11
+ s.description = "<%= app_name %> API client generated by gutsy from JSON Schema"
12
+
13
+ s.files = `git ls-files`.split "\n"
14
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split "\n"
15
+ s.require_paths = ['lib']
16
+
17
+ s.add_dependency 'multi_json'
18
+ s.add_dependency 'heroics', '~> 0.0.17'
19
+ s.add_dependency 'recursive-open-struct', '~> 1.0'
20
+ s.add_dependency 'excon', '>= 0.49.0', '< 1.0'
21
+
22
+ s.add_development_dependency 'rake'
23
+ end
@@ -0,0 +1,66 @@
1
+ module <%= gem_name_pascal %>
2
+ module V1
3
+ # Empty namespace, needed for heroics-generated HTTP client
4
+ module Adapters; end
5
+
6
+ module Adapter
7
+ def initialize(client=nil, access_token: nil, api_key: nil, api_secret: nil, options: {})
8
+ @client = if client
9
+ client
10
+ else
11
+ config = <%= gem_name_pascal %>.configuration
12
+ access_token ||= config.access_token
13
+ api_key ||= config.api_key
14
+ api_secret ||= config.api_secret
15
+ options = config.options.merge!(options)
16
+
17
+ # Workaround issue with some TLS setups (haproxy)
18
+ # Prevents :443 being added to hostname for TLS
19
+ Excon.defaults[:omit_default_port] = true
20
+
21
+ if access_token
22
+ config.adapter_factory.connect_oauth(access_token, options)
23
+ elsif api_key && api_secret
24
+ config.adapter_factory.connect(api_secret, options.merge!({
25
+ user: config.api_key
26
+ }))
27
+ else
28
+ raise 'access_token or API key & secret are required to connect to <%= app_name.downcase %> API'
29
+ end
30
+ end
31
+ end
32
+
33
+ def method_missing(name, *args)
34
+ if resource_caller.respond_to?(name)
35
+ begin
36
+ response = resource_caller.send(name, *args)
37
+ response[:ok?] = true
38
+ rescue Excon::Errors::UnprocessableEntity => e
39
+ response = MultiJson.load(e.response.body)
40
+ response[:ok?] = false
41
+ end
42
+
43
+ RecursiveOpenStruct.new(response, recurse_over_arrays: true)
44
+ else
45
+ raise NoMethodError.new("'#{resource_name}' API adapter '#{to_s}' does not respond to ##{name}")
46
+ end
47
+ end
48
+
49
+ def resource_name
50
+ raise "Please define a name for this resource"
51
+ end
52
+
53
+ def adapter_class
54
+ client.class
55
+ end
56
+
57
+ protected
58
+
59
+ attr_accessor :client
60
+
61
+ def resource_caller
62
+ client.send(resource_name)
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,11 @@
1
+ module <%= gem_name_pascal %>
2
+ module V1
3
+ class <%= resource_name.camelize %>
4
+ include Adapter
5
+
6
+ def resource_name
7
+ "<%= resource_name.underscore %>"
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ module <%= gem_name_pascal %>
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,58 @@
1
+ require 'multi_json'
2
+ require 'recursive-open-struct'
3
+ require '<%= gem_name_snake %>/v1/adapter'
4
+
5
+ Dir.glob(File.join(File.dirname(__FILE__), "<%= gem_name_snake %>", '**', '*.rb')).each do |file|
6
+ require file
7
+ end
8
+
9
+ module <%= gem_name_pascal %>
10
+ class << self
11
+ attr_accessor :configuration
12
+ end
13
+
14
+ # Configure <%= gem_name_pascal %> someplace sensible,
15
+ # like config/initializers/<%= gem_name_snake %>.rb
16
+ #
17
+ # @example
18
+ # <%= gem_name_pascal %>.configure do |config|
19
+ # config.base_url = 'http://<%= app_name.downcase %>.dev'
20
+ # # Specify credentials for OAuth browser-flow
21
+ # config.access_token = 'randomness'
22
+ # # or server-side flow
23
+ # config.api_key = 'i-am-app'
24
+ # config.api_secret = 'am-secret-dont-tell'
25
+ # end
26
+ def self.configure
27
+ self.configuration ||= Configuration.new
28
+ yield(configuration) if block_given?
29
+ end
30
+
31
+ class Configuration
32
+ attr_accessor :access_token, :api_key, :api_secret,
33
+ :base_url, :options,
34
+ :adapter_factory, :api_version
35
+
36
+ def options
37
+ @options ||= {}
38
+ end
39
+
40
+ def api_version
41
+ @api_version ||= 'v1'
42
+ end
43
+
44
+ def api_version=(version)
45
+ options[:url] = "#{base_url}/api/#{version}"
46
+ @api_version = version
47
+ end
48
+
49
+ def adapter_factory
50
+ @adapter_factory ||= <%= gem_name_pascal %>.const_get("#{api_version.upcase}")::Adapters::Http
51
+ end
52
+
53
+ def base_url=(url)
54
+ options[:url] = "#{url}/api/#{api_version}"
55
+ @base_url = url
56
+ end
57
+ end
58
+ end
metadata ADDED
@@ -0,0 +1,157 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gutsy
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Iora Health
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-08-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: heroics
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.0.17
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.0.17
27
+ - !ruby/object:Gem::Dependency
28
+ name: json_schema
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.13'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.13'
41
+ - !ruby/object:Gem::Dependency
42
+ name: activesupport
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '3.2'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '3.2'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.12'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.12'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '10.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '10.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3.0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '3.0'
97
+ description: Generates heroics-powered API client gem wrappers from JSON Schema
98
+ email:
99
+ - rubygems@iorahealth.com
100
+ executables:
101
+ - gutsy
102
+ extensions: []
103
+ extra_rdoc_files: []
104
+ files:
105
+ - ".gitignore"
106
+ - ".rspec"
107
+ - ".travis.yml"
108
+ - CODE_OF_CONDUCT.md
109
+ - Gemfile
110
+ - Gemfile.lock
111
+ - LICENSE.txt
112
+ - README.md
113
+ - Rakefile
114
+ - bin/console
115
+ - bin/gutsy
116
+ - bin/setup
117
+ - gutsy.gemspec
118
+ - lib/gutsy.rb
119
+ - lib/gutsy/cli.rb
120
+ - lib/gutsy/generator.rb
121
+ - lib/gutsy/version.rb
122
+ - templates/app_client/.gitignore.erb
123
+ - templates/app_client/Gemfile.erb
124
+ - templates/app_client/LICENSE.txt.erb
125
+ - templates/app_client/README.md.erb
126
+ - templates/app_client/Rakefile.erb
127
+ - templates/app_client/app_client.gemspec.erb
128
+ - templates/app_client/lib/app_client.rb.erb
129
+ - templates/app_client/lib/app_client/v1/adapter.rb.erb
130
+ - templates/app_client/lib/app_client/v1/adapters/.gitkeep
131
+ - templates/app_client/lib/app_client/v1/resource.rb.erb
132
+ - templates/app_client/lib/app_client/version.rb.erb
133
+ homepage: https://github.com/IoraHealth/gutsy
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: Generates heroics-powered API client gem wrappers from JSON Schema
157
+ test_files: []