gutsy 0.1.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +21 -0
- data/Gemfile.lock +8 -9
- data/README.md +4 -2
- data/examples/config.yml +17 -0
- data/lib/gutsy.rb +7 -59
- data/lib/gutsy/cli.rb +73 -3
- data/lib/gutsy/configuration.rb +17 -0
- data/lib/gutsy/generator.rb +48 -110
- data/lib/gutsy/generator/api_version_state.rb +37 -0
- data/lib/gutsy/generator/gem_state.rb +64 -0
- data/lib/gutsy/generator/heroics.rb +38 -0
- data/lib/gutsy/generator/resource_state.rb +23 -0
- data/lib/gutsy/schema.rb +36 -0
- data/lib/gutsy/version.rb +1 -1
- data/templates/app_client/README.md.erb +16 -8
- data/templates/app_client/app_client.gemspec.erb +8 -2
- data/templates/app_client/lib/app_client.rb.erb +11 -3
- data/templates/app_client/lib/app_client/{v1 → api_version}/adapter.rb.erb +9 -3
- data/templates/app_client/lib/app_client/{v1 → api_version}/adapters/.gitkeep +0 -0
- data/templates/app_client/lib/app_client/{v1 → api_version}/resource.rb.erb +1 -1
- data/templates/app_client/lib/app_client/version.rb.erb +1 -1
- metadata +13 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a3cacfdafb8ce58f27fe22fb41bd170512edc36c
|
4
|
+
data.tar.gz: f0dde3a28b3c519a8bed2e71c567f32f911d48aa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6b44d4dadc00b9bc395f28e5fbae3271800a3ae8bac1b0121c5e4c7358112ebb06b38bcc5c8aa4e0f679cef35a8e237692c5e6dc149e7e08d64d0faab339a2df
|
7
|
+
data.tar.gz: cccc382d21b5f94a5edfb6c1c1092db43753857bd1be3fadbf770c1cf050f5412c1403a4b5fa0c62c26da0036d299df2fe1fbb843a142e3ecb19fa36551635fc
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# Gutsy Changelog
|
2
|
+
|
3
|
+
## 1.0.0
|
4
|
+
|
5
|
+
* Support multiple API versions and apps in one configuration file, allow configuring gem metadata. (#1)
|
6
|
+
|
7
|
+
### Breaking Changes
|
8
|
+
|
9
|
+
* The CLI API for `gutsy generate` has changed.
|
10
|
+
|
11
|
+
In 0.1.0, you could do `gutsy generate AppName /path/to/schema.json /path/for/output/`.
|
12
|
+
|
13
|
+
As of 1.0.0, gutsy now allows a much greater degree of customization, through a YAML configuration file.
|
14
|
+
The interface is now `gutsy generate /path/to/gutsy/config.yml /path/for/output/`.
|
15
|
+
|
16
|
+
See [`examples/config.yml`](examples/config.yml) for an example of a Gutsy configuration file.
|
17
|
+
|
18
|
+
## 0.1.0
|
19
|
+
|
20
|
+
* Initial release
|
21
|
+
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
gutsy (
|
4
|
+
gutsy (1.0.0)
|
5
5
|
activesupport (>= 3.2)
|
6
6
|
heroics (~> 0.0.17)
|
7
7
|
json_schema (~> 0.13)
|
@@ -9,15 +9,15 @@ PATH
|
|
9
9
|
GEM
|
10
10
|
remote: https://rubygems.org/
|
11
11
|
specs:
|
12
|
-
activesupport (
|
13
|
-
|
14
|
-
|
12
|
+
activesupport (5.0.0.1)
|
13
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
14
|
+
i18n (~> 0.7)
|
15
15
|
minitest (~> 5.1)
|
16
|
-
thread_safe (~> 0.1)
|
17
16
|
tzinfo (~> 1.1)
|
17
|
+
concurrent-ruby (1.0.2)
|
18
18
|
diff-lcs (1.2.5)
|
19
19
|
erubis (2.7.0)
|
20
|
-
excon (0.
|
20
|
+
excon (0.52.0)
|
21
21
|
heroics (0.0.17)
|
22
22
|
erubis (~> 2.0)
|
23
23
|
excon
|
@@ -25,9 +25,8 @@ GEM
|
|
25
25
|
multi_json (>= 1.9.2)
|
26
26
|
netrc
|
27
27
|
i18n (0.7.0)
|
28
|
-
|
29
|
-
|
30
|
-
minitest (5.8.3)
|
28
|
+
json_schema (0.13.3)
|
29
|
+
minitest (5.9.0)
|
31
30
|
moneta (0.8.0)
|
32
31
|
multi_json (1.12.1)
|
33
32
|
netrc (0.11.0)
|
data/README.md
CHANGED
@@ -48,10 +48,12 @@ Gutsy generates RubyGem wrappers and documentation for [heroics](https://github.
|
|
48
48
|
|
49
49
|
```bash
|
50
50
|
$ gem install gutsy
|
51
|
-
$ gutsy generate
|
51
|
+
$ gutsy generate /path/to/gutsy/config.yml /path/for/output/
|
52
52
|
```
|
53
53
|
|
54
|
-
|
54
|
+
See [`examples/config.yml`](examples/config.yml) for an example of a Gutsy configuration file.
|
55
|
+
|
56
|
+
Check out your generated API gem!
|
55
57
|
|
56
58
|
## License
|
57
59
|
|
data/examples/config.yml
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
---
|
2
|
+
gutsy:
|
3
|
+
feature_toggles:
|
4
|
+
name: FeatureToggles
|
5
|
+
base_url: 'http://features.dev' # App base URL (no trailing slash)
|
6
|
+
description: | # Description, optional
|
7
|
+
FeatureToggles toggles features. This gem provides a wrapper around an HTTP
|
8
|
+
Client generated from FeatureToggles's JSON Schema using
|
9
|
+
[heroics](https://github.com/interagent/heroics).
|
10
|
+
author: # Gem author
|
11
|
+
name: Iora Health
|
12
|
+
email: rubygems@iorahealth.com
|
13
|
+
github: IoraHealth
|
14
|
+
version: 0.2.0 # Gem version
|
15
|
+
versions:
|
16
|
+
- name: v1
|
17
|
+
schema_path: docs/api/v1/schema.json
|
data/lib/gutsy.rb
CHANGED
@@ -1,73 +1,21 @@
|
|
1
1
|
require 'active_support'
|
2
|
+
require 'active_support/core_ext/hash/keys'
|
2
3
|
require 'active_support/core_ext/object/try'
|
3
4
|
require 'active_support/core_ext/string/inflections'
|
4
5
|
require 'erb'
|
5
6
|
require 'forwardable'
|
6
7
|
require 'json_schema'
|
7
8
|
require 'open-uri'
|
9
|
+
require 'yaml'
|
10
|
+
|
11
|
+
require 'gutsy/configuration'
|
12
|
+
require 'gutsy/schema'
|
13
|
+
require 'gutsy/generator'
|
8
14
|
require 'gutsy/version'
|
9
15
|
require 'gutsy/cli'
|
10
16
|
|
11
17
|
module Gutsy
|
12
18
|
def self.initialize!
|
13
|
-
|
14
|
-
|
15
|
-
command = args[0]
|
16
|
-
|
17
|
-
case command
|
18
|
-
when "generate"
|
19
|
-
unless args.length == 4
|
20
|
-
puts <<-TEXT
|
21
|
-
Error: Not enough arguments for command 'generate'
|
22
|
-
|
23
|
-
Usage: gutsy generate [app_name] [schema_path] [output_path]
|
24
|
-
|
25
|
-
DESCRIPTION
|
26
|
-
Generates a gem scaffold and resource API clients on top of a heroics-generated client.
|
27
|
-
|
28
|
-
ARGUMENTS
|
29
|
-
[app_name] - CamelCased name of your application
|
30
|
-
[schema_path] - Path to your JSON Schema file
|
31
|
-
[output_path] - Path to output generated API client gem.
|
32
|
-
(Will be created if it doesn't exist)
|
33
|
-
TEXT
|
34
|
-
exit 1
|
35
|
-
end
|
36
|
-
app_name = args[1]
|
37
|
-
schema_path = File.expand_path(args[2])
|
38
|
-
output_path = File.expand_path(args[3])
|
39
|
-
|
40
|
-
generator = Gutsy::Cli::Generator.new(app_name, schema_path, output_path)
|
41
|
-
|
42
|
-
begin
|
43
|
-
generator.generate!
|
44
|
-
rescue => e
|
45
|
-
puts "FAIL"
|
46
|
-
puts e.message
|
47
|
-
puts e.backtrace.join("\n")
|
48
|
-
exit 1
|
49
|
-
end
|
50
|
-
|
51
|
-
exit 0
|
52
|
-
when "version"
|
53
|
-
puts "Gutsy version #{Gutsy::VERSION}"
|
54
|
-
exit 0
|
55
|
-
else
|
56
|
-
puts <<-TEXT
|
57
|
-
Usage: gutsy [command] [options]
|
58
|
-
|
59
|
-
DESCRIPTION
|
60
|
-
Generates gem wrappers around heroics-generated API clients
|
61
|
-
built with JSON Schema. (Enough layers of generation for ya?)
|
62
|
-
|
63
|
-
COMMANDS
|
64
|
-
generate scaffolds out an API client
|
65
|
-
version returns the gutsy version
|
66
|
-
help displays this message
|
67
|
-
|
68
|
-
Shouts out Mr. Gutsy. Keep on plugging in the Wasteland.
|
69
|
-
TEXT
|
70
|
-
exit 0
|
71
|
-
end
|
19
|
+
Gutsy::Cli.parse!(ARGV)
|
72
20
|
end
|
73
21
|
end
|
data/lib/gutsy/cli.rb
CHANGED
@@ -1,7 +1,77 @@
|
|
1
1
|
module Gutsy
|
2
2
|
module Cli
|
3
|
-
|
3
|
+
def self.parse!(args)
|
4
|
+
command = args[0]
|
5
|
+
|
6
|
+
case command
|
7
|
+
when "generate"
|
8
|
+
generate(args[1..-1])
|
9
|
+
when "version"
|
10
|
+
version
|
11
|
+
else
|
12
|
+
help
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.generate(args)
|
17
|
+
unless args.length == 2
|
18
|
+
puts <<-TEXT
|
19
|
+
Error: Not enough arguments for command 'generate'
|
20
|
+
|
21
|
+
Usage: gutsy generate [config_path] [output_path]
|
22
|
+
|
23
|
+
DESCRIPTION
|
24
|
+
Generates a gem scaffold and resource API clients on top of a heroics-generated client.
|
25
|
+
|
26
|
+
ARGUMENTS
|
27
|
+
[config_path] - Path to gutsy configuration file
|
28
|
+
[output_path] - Path to output generated API client gem(s).
|
29
|
+
(Will be created if it doesn't exist)
|
30
|
+
TEXT
|
31
|
+
exit 1
|
32
|
+
end
|
33
|
+
|
34
|
+
config_path = File.expand_path(args[0])
|
35
|
+
output_path = File.expand_path(args[1])
|
36
|
+
|
37
|
+
config = Gutsy::Configuration.load_from_file!(config_path)
|
38
|
+
|
39
|
+
config.apps.each do |app_config|
|
40
|
+
generator = Gutsy::Cli::Generator.new(app_config, output_path)
|
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
|
+
end
|
50
|
+
|
51
|
+
exit 0
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.version
|
55
|
+
puts "Gutsy version #{Gutsy::VERSION}"
|
56
|
+
exit 0
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.help
|
60
|
+
puts <<-TEXT
|
61
|
+
Usage: gutsy [command] [options]
|
62
|
+
|
63
|
+
DESCRIPTION
|
64
|
+
Generates gem wrappers around heroics-generated API clients
|
65
|
+
built with JSON Schema. (Enough layers of generation for ya?)
|
66
|
+
|
67
|
+
COMMANDS
|
68
|
+
generate scaffolds out an API client
|
69
|
+
version returns the gutsy version
|
70
|
+
help displays this message
|
71
|
+
|
72
|
+
Shouts out Mr. Gutsy. Keep on plugging in the Wasteland.
|
73
|
+
TEXT
|
74
|
+
exit 0
|
75
|
+
end
|
4
76
|
end
|
5
77
|
end
|
6
|
-
|
7
|
-
require 'gutsy/generator'
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Gutsy
|
2
|
+
class Configuration
|
3
|
+
def self.load_from_file!(config_file_path)
|
4
|
+
yaml_config = YAML.load_file(config_file_path).deep_symbolize_keys
|
5
|
+
raise "Not a valid gutsy configration file" unless yaml_config[:gutsy]
|
6
|
+
new(yaml_config[:gutsy])
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(config)
|
10
|
+
@config = config
|
11
|
+
end
|
12
|
+
|
13
|
+
def apps
|
14
|
+
@config.values
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/gutsy/generator.rb
CHANGED
@@ -1,81 +1,29 @@
|
|
1
|
+
require 'gutsy/generator/api_version_state'
|
2
|
+
require 'gutsy/generator/gem_state'
|
3
|
+
require 'gutsy/generator/resource_state'
|
4
|
+
require 'gutsy/generator/heroics'
|
5
|
+
|
1
6
|
module Gutsy
|
2
7
|
module Cli
|
3
8
|
class Generator
|
4
9
|
extend Forwardable
|
5
10
|
|
6
|
-
|
7
|
-
|
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
|
11
|
+
def initialize(app_config, output_path)
|
12
|
+
@state = Gutsy::Generator::GemState.new(app_config)
|
58
13
|
@output_path = output_path
|
59
14
|
end
|
60
15
|
|
61
16
|
def generate!
|
62
17
|
create_output_dir
|
63
18
|
|
64
|
-
|
65
|
-
|
66
|
-
state.resources = map_schema_to_resources(schema)
|
67
|
-
|
68
|
-
scaffold_gem
|
69
|
-
|
70
|
-
generate_heroics_client
|
19
|
+
build_gem
|
71
20
|
|
72
21
|
puts "Generated client gem can be found in... #{output_path}"
|
73
22
|
end
|
74
23
|
|
75
24
|
private
|
76
25
|
|
77
|
-
attr_reader :state, :
|
78
|
-
attr_accessor :schema
|
26
|
+
attr_reader :state, :output_path
|
79
27
|
def_delegators :state, :app_name, :gem_name_snake, :gem_name_pascal
|
80
28
|
|
81
29
|
def create_output_dir
|
@@ -84,43 +32,35 @@ module Gutsy
|
|
84
32
|
puts "OK"
|
85
33
|
end
|
86
34
|
|
87
|
-
def
|
88
|
-
print "
|
89
|
-
|
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)
|
35
|
+
def build_gem
|
36
|
+
print "Creating gem directory structure..."
|
37
|
+
build_gem_directory_tree
|
98
38
|
puts "OK"
|
99
39
|
|
100
|
-
|
101
|
-
|
40
|
+
print "Creating gem metadata..."
|
41
|
+
generate_gem_metadata
|
42
|
+
puts "OK"
|
102
43
|
|
103
|
-
|
104
|
-
|
105
|
-
|
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
|
44
|
+
print "Generating API clients for each API version..."
|
45
|
+
generate_api_clients
|
46
|
+
puts "OK"
|
113
47
|
end
|
114
48
|
|
115
|
-
def
|
116
|
-
|
117
|
-
template_dirs.each do |dir|
|
49
|
+
def build_gem_directory_tree
|
50
|
+
template_dirs.flat_map do |dir|
|
118
51
|
dir = dir.gsub('app_client', gem_name_snake)
|
52
|
+
if dir =~ /api_version/
|
53
|
+
state.api_versions.map { |v| dir.gsub('api_version', v.name) }
|
54
|
+
else
|
55
|
+
[dir]
|
56
|
+
end
|
57
|
+
end.each do |dir|
|
119
58
|
dir_path = File.join(output_path, dir)
|
120
59
|
Dir.mkdir(dir_path, 0755) unless Dir.exist?(dir_path)
|
121
60
|
end
|
122
|
-
|
61
|
+
end
|
123
62
|
|
63
|
+
def generate_gem_metadata
|
124
64
|
[
|
125
65
|
".gitignore",
|
126
66
|
"Gemfile",
|
@@ -131,34 +71,32 @@ module Gutsy
|
|
131
71
|
copy_file file
|
132
72
|
end
|
133
73
|
|
134
|
-
copy_file "app_client.gemspec",
|
135
|
-
|
74
|
+
copy_file "app_client.gemspec", as: "#{gem_name_snake}.gemspec"
|
75
|
+
copy_file "lib/app_client.rb", as: "lib/#{gem_name_snake}.rb"
|
76
|
+
copy_file "lib/app_client/version.rb", as: "lib/#{gem_name_snake}/version.rb"
|
77
|
+
end
|
136
78
|
|
137
|
-
|
138
|
-
|
79
|
+
def generate_api_clients
|
80
|
+
state.api_versions.each do |api_version|
|
81
|
+
copy_file "lib/app_client/api_version/adapter.rb",
|
82
|
+
as: "lib/#{gem_name_snake}/#{api_version.name}/adapter.rb",
|
83
|
+
binding: api_version.twine
|
139
84
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
85
|
+
api_version.resources.each do |key, resource|
|
86
|
+
resource_state = Gutsy::Generator::ResourceState.new(key.to_s, api_version)
|
87
|
+
|
88
|
+
copy_file "lib/app_client/api_version/resource.rb",
|
89
|
+
as: "lib/#{gem_name_snake}/#{api_version.name}/#{key.to_s.underscore}.rb",
|
90
|
+
binding: resource_state.twine
|
91
|
+
end
|
147
92
|
|
148
|
-
|
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
|
93
|
+
generate_heroics_client(api_version)
|
152
94
|
end
|
153
95
|
end
|
154
96
|
|
155
|
-
def generate_heroics_client
|
156
|
-
print "Generating Heroics client for JSON Schema..."
|
157
|
-
unless
|
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"
|
97
|
+
def generate_heroics_client(api_version)
|
98
|
+
print "Generating Heroics client for #{api_version.name} JSON Schema..."
|
99
|
+
unless Gutsy::Generator::Heroics.generate(api_version, output_path)
|
162
100
|
puts "FAIL"
|
163
101
|
puts "Please see stacktrace or heroics errors"
|
164
102
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Gutsy
|
2
|
+
module Generator
|
3
|
+
class ApiVersionState
|
4
|
+
extend Forwardable
|
5
|
+
|
6
|
+
attr_reader :name, :schema, :schema_path
|
7
|
+
|
8
|
+
def_delegators :gem_state, :gem_name_pascal, :gem_name_snake, :app_name, :base_url
|
9
|
+
|
10
|
+
def initialize(api_version_config, gem_state)
|
11
|
+
@name = api_version_config[:name]
|
12
|
+
@schema_path = api_version_config[:schema_path]
|
13
|
+
@gem_state = gem_state
|
14
|
+
end
|
15
|
+
|
16
|
+
def module_name
|
17
|
+
@module_name ||= name.upcase
|
18
|
+
end
|
19
|
+
|
20
|
+
def schema
|
21
|
+
@schema ||= Gutsy::Schema.load_from_file!(schema_path)
|
22
|
+
end
|
23
|
+
|
24
|
+
def resources
|
25
|
+
schema.resources
|
26
|
+
end
|
27
|
+
|
28
|
+
def twine
|
29
|
+
binding
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
attr_reader :gem_state
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Gutsy
|
2
|
+
module Generator
|
3
|
+
class GemState
|
4
|
+
attr_reader :app_name, :base_url, :description
|
5
|
+
|
6
|
+
def initialize(app_config)
|
7
|
+
@app_name = app_config[:name]
|
8
|
+
@base_url = app_config[:base_url]
|
9
|
+
@description = app_config[:description]
|
10
|
+
@app_config = app_config
|
11
|
+
end
|
12
|
+
|
13
|
+
def api_versions
|
14
|
+
@api_versions ||= app_config[:versions].map do |api_version_config|
|
15
|
+
Gutsy::Generator::ApiVersionState.new(api_version_config, self)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def gem_name
|
20
|
+
@gem_name_snake ||= "#{app_name.underscore}_client"
|
21
|
+
end
|
22
|
+
|
23
|
+
def gem_name_snake
|
24
|
+
gem_name
|
25
|
+
end
|
26
|
+
|
27
|
+
def gem_name_pascal
|
28
|
+
@gem_name_pascal ||= gem_name.camelize(:upper)
|
29
|
+
end
|
30
|
+
|
31
|
+
def gem_version
|
32
|
+
@gem_version ||= app_config[:gem_version] || '0.1.0'
|
33
|
+
end
|
34
|
+
|
35
|
+
def copyright_year
|
36
|
+
@copyright_year ||= Time.now.year
|
37
|
+
end
|
38
|
+
|
39
|
+
def copyright_owner
|
40
|
+
@copyright_owner ||= author.name
|
41
|
+
end
|
42
|
+
|
43
|
+
def author
|
44
|
+
@author ||= OpenStruct.new(author_config)
|
45
|
+
end
|
46
|
+
|
47
|
+
def twine
|
48
|
+
binding
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
attr_reader :app_config
|
54
|
+
|
55
|
+
def author_config
|
56
|
+
app_config[:author] || {
|
57
|
+
name: "YOUR_NAME_HERE",
|
58
|
+
email: "YOUR_EMAIL_ADDRESS",
|
59
|
+
github: "GITHUB_USER"
|
60
|
+
}
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Gutsy
|
2
|
+
module Generator
|
3
|
+
class Heroics
|
4
|
+
def self.generate(api_version_state, output_path)
|
5
|
+
new(api_version_state, output_path).generate
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize(api_version_state, output_path)
|
9
|
+
@api_version_state = api_version_state
|
10
|
+
@output_path = output_path
|
11
|
+
end
|
12
|
+
|
13
|
+
def generate
|
14
|
+
system "heroics-generate \
|
15
|
+
#{module_name} \
|
16
|
+
#{api_version_state.schema_path} \
|
17
|
+
#{api_url} > \
|
18
|
+
#{client_output_path}"
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
attr_reader :api_version_state, :output_path
|
24
|
+
|
25
|
+
def module_name
|
26
|
+
@module_name ||= "#{api_version_state.gem_name_pascal}::#{api_version_state.module_name}::Adapters::Http"
|
27
|
+
end
|
28
|
+
|
29
|
+
def api_url
|
30
|
+
@api_url ||= "#{api_version_state.base_url}/api/#{api_version_state.name.downcase}"
|
31
|
+
end
|
32
|
+
|
33
|
+
def client_output_path
|
34
|
+
@client_output_path ||= "#{output_path}/lib/#{api_version_state.gem_name_snake}/#{api_version_state.name.downcase}/adapters/http.rb"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Gutsy
|
2
|
+
module Generator
|
3
|
+
class ResourceState
|
4
|
+
extend Forwardable
|
5
|
+
|
6
|
+
attr_reader :resource_name
|
7
|
+
def_delegators :version_state, :gem_name_pascal, :app_name, :module_name
|
8
|
+
|
9
|
+
def initialize(resource_name, version_state)
|
10
|
+
@resource_name = resource_name
|
11
|
+
@version_state = version_state
|
12
|
+
end
|
13
|
+
|
14
|
+
def twine
|
15
|
+
binding
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
attr_reader :version_state
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/gutsy/schema.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
module Gutsy
|
2
|
+
class Schema
|
3
|
+
def self.load_from_file!(schema_path)
|
4
|
+
draft04_uri = URI.parse("http://json-schema.org/draft-04/hyper-schema")
|
5
|
+
draft04 = JsonSchema.parse!(JSON.parse(draft04_uri.read))
|
6
|
+
|
7
|
+
schema_json = JSON.parse(File.read(schema_path))
|
8
|
+
|
9
|
+
schema = JsonSchema.parse!(schema_json)
|
10
|
+
schema.expand_references!
|
11
|
+
|
12
|
+
draft04.validate!(schema)
|
13
|
+
|
14
|
+
new(schema)
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(json_schema)
|
18
|
+
@schema = json_schema
|
19
|
+
end
|
20
|
+
|
21
|
+
def resources
|
22
|
+
@resources ||= Hash[schema.definitions.map do |key, resource|
|
23
|
+
links = Hash[resource.links.map do |link|
|
24
|
+
link.schema.expand_references! if link.schema
|
25
|
+
properties = link.schema.try(:properties) || {}
|
26
|
+
[link.title.downcase.to_sym, OpenStruct.new(properties: properties)]
|
27
|
+
end]
|
28
|
+
[key.to_sym, OpenStruct.new(title: key.camelize, links: links)]
|
29
|
+
end]
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
attr_reader :schema
|
35
|
+
end
|
36
|
+
end
|
data/lib/gutsy/version.rb
CHANGED
@@ -1,14 +1,19 @@
|
|
1
1
|
# <%= app_name %> Ruby Client
|
2
2
|
|
3
|
+
<% if description -%>
|
4
|
+
<%= description -%>
|
5
|
+
<% else -%>
|
3
6
|
<%= 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
|
-
|
7
|
+
<% end -%>
|
5
8
|
|
6
9
|
## Usage
|
7
10
|
|
8
11
|
Add this to your Gemfile
|
9
12
|
|
10
13
|
```
|
11
|
-
gem '<%= gem_name_snake %>'
|
14
|
+
gem '<%= gem_name_snake %>', git: 'git@github.com:<%= author.github %>/<%= gem_name_snake %>.git', tag: 'v<%= gem_version %>'
|
15
|
+
# or, depending on how cool your API is...
|
16
|
+
gem '<%= gem_name_snake %>', '~> <%= gem_version %>'
|
12
17
|
```
|
13
18
|
|
14
19
|
### Rails Setup
|
@@ -17,11 +22,11 @@ gem '<%= gem_name_snake %>' #, git: 'git@github.com:USER/REPO.git', tag: 'v0.1.0
|
|
17
22
|
|
18
23
|
```ruby
|
19
24
|
<%= gem_name_pascal %>.configure do |config|
|
20
|
-
config.base_url = "
|
21
|
-
config.api_key = "i-am-api-user"
|
22
|
-
config.api_secret = "very-secret-sssshhh"
|
25
|
+
config.base_url = "<%= base_url %>"
|
26
|
+
config.api_key = "i-am-api-user" # HTTP Basic Auth User
|
27
|
+
config.api_secret = "very-secret-sssshhh" # HTTP Basic Auth Pass
|
23
28
|
# or OAuth access token
|
24
|
-
# config.access_token =
|
29
|
+
# config.access_token = ENV['<%= gem_name_snake.upcase %>_ACCESS_TOKEN']
|
25
30
|
end
|
26
31
|
```
|
27
32
|
|
@@ -47,14 +52,16 @@ class SomeResourceLister
|
|
47
52
|
end
|
48
53
|
```
|
49
54
|
|
50
|
-
|
55
|
+
# API
|
51
56
|
|
52
57
|
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
58
|
|
54
59
|
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
60
|
|
56
|
-
<%
|
61
|
+
<% api_versions.each do |api_version| %>
|
62
|
+
## <%= app_name %> API <%= api_version.name %>
|
57
63
|
|
64
|
+
<% api_version.resources.each do |key, resource| -%>
|
58
65
|
### <%= resource.title.camelize %>
|
59
66
|
|
60
67
|
#### `::new(client=nil, access_token: nil, api_key: nil, api_secret: nil, options: {})`
|
@@ -88,4 +95,5 @@ Called with no arguments, `client` defaults to a newly created heroics client in
|
|
88
95
|
<% end -%>
|
89
96
|
<% end -%>
|
90
97
|
<% end -%>
|
98
|
+
<% end -%>
|
91
99
|
<% end -%>
|
@@ -5,10 +5,16 @@ require "./lib/<%= gem_name_snake %>/version"
|
|
5
5
|
Gem::Specification.new do |s|
|
6
6
|
s.name = '<%= gem_name_snake %>'
|
7
7
|
s.version = <%= gem_name_pascal %>::VERSION
|
8
|
-
s.authors = ["
|
9
|
-
s.email = "author
|
8
|
+
s.authors = ["<%= author.name %>"]
|
9
|
+
s.email = "<%= author.email %>"
|
10
10
|
s.summary = "Gutsy-generated gem for <%= app_name %> client"
|
11
|
+
<% if description -%>
|
12
|
+
s.description = <<-DESCRIPTION
|
13
|
+
<%= description -%>
|
14
|
+
DESCRIPTION
|
15
|
+
<% else -%>
|
11
16
|
s.description = "<%= app_name %> API client generated by gutsy from JSON Schema"
|
17
|
+
<% end -%>
|
12
18
|
|
13
19
|
s.files = `git ls-files`.split "\n"
|
14
20
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split "\n"
|
@@ -1,6 +1,14 @@
|
|
1
|
+
#
|
2
|
+
# HEADS UP: Do not edit by hand (or do, I'm not your parent)
|
3
|
+
# This file was generated by Gutsy:
|
4
|
+
# https://github.com/IoraHealth/gutsy
|
5
|
+
#
|
6
|
+
|
1
7
|
require 'multi_json'
|
2
8
|
require 'recursive-open-struct'
|
3
|
-
|
9
|
+
<% api_versions.each do |version| -%>
|
10
|
+
require '<%= gem_name_snake %>/<%= version.name %>/adapter'
|
11
|
+
<% end -%>
|
4
12
|
|
5
13
|
Dir.glob(File.join(File.dirname(__FILE__), "<%= gem_name_snake %>", '**', '*.rb')).each do |file|
|
6
14
|
require file
|
@@ -16,7 +24,7 @@ module <%= gem_name_pascal %>
|
|
16
24
|
#
|
17
25
|
# @example
|
18
26
|
# <%= gem_name_pascal %>.configure do |config|
|
19
|
-
# config.base_url = '
|
27
|
+
# config.base_url = '<%= base_url %>'
|
20
28
|
# # Specify credentials for OAuth browser-flow
|
21
29
|
# config.access_token = 'randomness'
|
22
30
|
# # or server-side flow
|
@@ -38,7 +46,7 @@ module <%= gem_name_pascal %>
|
|
38
46
|
end
|
39
47
|
|
40
48
|
def api_version
|
41
|
-
@api_version ||= '
|
49
|
+
@api_version ||= '<%= api_versions.first.name %>'
|
42
50
|
end
|
43
51
|
|
44
52
|
def api_version=(version)
|
@@ -1,5 +1,11 @@
|
|
1
|
+
#
|
2
|
+
# HEADS UP: Do not edit by hand (or do, I'm not your parent)
|
3
|
+
# This file was generated by Gutsy:
|
4
|
+
# https://github.com/IoraHealth/gutsy
|
5
|
+
#
|
6
|
+
|
1
7
|
module <%= gem_name_pascal %>
|
2
|
-
module
|
8
|
+
module <%= module_name %>
|
3
9
|
# Empty namespace, needed for heroics-generated HTTP client
|
4
10
|
module Adapters; end
|
5
11
|
|
@@ -25,7 +31,7 @@ module <%= gem_name_pascal %>
|
|
25
31
|
user: config.api_key
|
26
32
|
}))
|
27
33
|
else
|
28
|
-
raise
|
34
|
+
raise "access_token or API key & secret are required to connect to <%= app_name %> <%= module_name %> API"
|
29
35
|
end
|
30
36
|
end
|
31
37
|
end
|
@@ -42,7 +48,7 @@ module <%= gem_name_pascal %>
|
|
42
48
|
|
43
49
|
RecursiveOpenStruct.new(response, recurse_over_arrays: true)
|
44
50
|
else
|
45
|
-
raise NoMethodError.new("'#{resource_name}' API adapter '#{to_s}' does not respond to ##{name}")
|
51
|
+
raise NoMethodError.new("<%= app_name %> <%= module_name%> '#{resource_name}' API adapter '#{to_s}' does not respond to ##{name}")
|
46
52
|
end
|
47
53
|
end
|
48
54
|
|
File without changes
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gutsy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Iora Health
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-02-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: heroics
|
@@ -105,6 +105,7 @@ files:
|
|
105
105
|
- ".gitignore"
|
106
106
|
- ".rspec"
|
107
107
|
- ".travis.yml"
|
108
|
+
- CHANGELOG.md
|
108
109
|
- CODE_OF_CONDUCT.md
|
109
110
|
- Gemfile
|
110
111
|
- Gemfile.lock
|
@@ -114,10 +115,17 @@ files:
|
|
114
115
|
- bin/console
|
115
116
|
- bin/gutsy
|
116
117
|
- bin/setup
|
118
|
+
- examples/config.yml
|
117
119
|
- gutsy.gemspec
|
118
120
|
- lib/gutsy.rb
|
119
121
|
- lib/gutsy/cli.rb
|
122
|
+
- lib/gutsy/configuration.rb
|
120
123
|
- lib/gutsy/generator.rb
|
124
|
+
- lib/gutsy/generator/api_version_state.rb
|
125
|
+
- lib/gutsy/generator/gem_state.rb
|
126
|
+
- lib/gutsy/generator/heroics.rb
|
127
|
+
- lib/gutsy/generator/resource_state.rb
|
128
|
+
- lib/gutsy/schema.rb
|
121
129
|
- lib/gutsy/version.rb
|
122
130
|
- templates/app_client/.gitignore.erb
|
123
131
|
- templates/app_client/Gemfile.erb
|
@@ -126,9 +134,9 @@ files:
|
|
126
134
|
- templates/app_client/Rakefile.erb
|
127
135
|
- templates/app_client/app_client.gemspec.erb
|
128
136
|
- templates/app_client/lib/app_client.rb.erb
|
129
|
-
- templates/app_client/lib/app_client/
|
130
|
-
- templates/app_client/lib/app_client/
|
131
|
-
- templates/app_client/lib/app_client/
|
137
|
+
- templates/app_client/lib/app_client/api_version/adapter.rb.erb
|
138
|
+
- templates/app_client/lib/app_client/api_version/adapters/.gitkeep
|
139
|
+
- templates/app_client/lib/app_client/api_version/resource.rb.erb
|
132
140
|
- templates/app_client/lib/app_client/version.rb.erb
|
133
141
|
homepage: https://github.com/IoraHealth/gutsy
|
134
142
|
licenses:
|