atum 0.5.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.
Files changed (76) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +16 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +9 -0
  5. data/.rubocop_todo.yml +25 -0
  6. data/.travis.yml +9 -0
  7. data/Appraisals +9 -0
  8. data/CHANGELOG.md +5 -0
  9. data/CONTRIBUTING.md +9 -0
  10. data/CONTRIBUTORS.md +15 -0
  11. data/Gemfile +4 -0
  12. data/Guardfile +23 -0
  13. data/LICENSE.txt +22 -0
  14. data/README.md +95 -0
  15. data/Rakefile +13 -0
  16. data/TODO +3 -0
  17. data/atum.gemspec +37 -0
  18. data/bin/atum +41 -0
  19. data/circle.yml +7 -0
  20. data/gemfiles/faraday_0.8.9.gemfile +8 -0
  21. data/gemfiles/faraday_0.9.gemfile +8 -0
  22. data/lib/atum.rb +15 -0
  23. data/lib/atum/core.rb +13 -0
  24. data/lib/atum/core/client.rb +45 -0
  25. data/lib/atum/core/errors.rb +20 -0
  26. data/lib/atum/core/link.rb +77 -0
  27. data/lib/atum/core/paginator.rb +32 -0
  28. data/lib/atum/core/request.rb +55 -0
  29. data/lib/atum/core/resource.rb +15 -0
  30. data/lib/atum/core/response.rb +53 -0
  31. data/lib/atum/core/schema.rb +12 -0
  32. data/lib/atum/core/schema/api_schema.rb +62 -0
  33. data/lib/atum/core/schema/link_schema.rb +121 -0
  34. data/lib/atum/core/schema/parameter.rb +27 -0
  35. data/lib/atum/core/schema/parameter_choice.rb +28 -0
  36. data/lib/atum/core/schema/resource_schema.rb +51 -0
  37. data/lib/atum/generation.rb +15 -0
  38. data/lib/atum/generation/erb_context.rb +17 -0
  39. data/lib/atum/generation/errors.rb +6 -0
  40. data/lib/atum/generation/generator_link.rb +39 -0
  41. data/lib/atum/generation/generator_resource.rb +31 -0
  42. data/lib/atum/generation/generator_service.rb +73 -0
  43. data/lib/atum/generation/generators/base_generator.rb +57 -0
  44. data/lib/atum/generation/generators/client_generator.rb +16 -0
  45. data/lib/atum/generation/generators/module_generator.rb +17 -0
  46. data/lib/atum/generation/generators/resource_generator.rb +23 -0
  47. data/lib/atum/generation/generators/views/client.erb +26 -0
  48. data/lib/atum/generation/generators/views/module.erb +104 -0
  49. data/lib/atum/generation/generators/views/resource.erb +33 -0
  50. data/lib/atum/generation/options_parameter.rb +12 -0
  51. data/lib/atum/version.rb +3 -0
  52. data/spec/atum/core/client_spec.rb +26 -0
  53. data/spec/atum/core/errors_spec.rb +19 -0
  54. data/spec/atum/core/link_spec.rb +80 -0
  55. data/spec/atum/core/paginator_spec.rb +72 -0
  56. data/spec/atum/core/request_spec.rb +110 -0
  57. data/spec/atum/core/resource_spec.rb +66 -0
  58. data/spec/atum/core/response_spec.rb +127 -0
  59. data/spec/atum/core/schema/api_schema_spec.rb +49 -0
  60. data/spec/atum/core/schema/link_schema_spec.rb +91 -0
  61. data/spec/atum/core/schema/parameter_choice_spec.rb +40 -0
  62. data/spec/atum/core/schema/parameter_spec.rb +24 -0
  63. data/spec/atum/core/schema/resource_schema_spec.rb +24 -0
  64. data/spec/atum/generation/generator_link_spec.rb +62 -0
  65. data/spec/atum/generation/generator_resource_spec.rb +44 -0
  66. data/spec/atum/generation/generator_service_spec.rb +41 -0
  67. data/spec/atum/generation/generators/base_generator_spec.rb +75 -0
  68. data/spec/atum/generation/generators/client_generator_spec.rb +30 -0
  69. data/spec/atum/generation/generators/module_generator_spec.rb +37 -0
  70. data/spec/atum/generation/generators/resource_generator_spec.rb +46 -0
  71. data/spec/atum/generation/options_parameter_spec.rb +27 -0
  72. data/spec/fixtures/fruity_schema.json +161 -0
  73. data/spec/fixtures/sample_schema.json +139 -0
  74. data/spec/integration/client_integration_spec.rb +91 -0
  75. data/spec/spec_helper.rb +11 -0
  76. metadata +303 -0
@@ -0,0 +1,44 @@
1
+ require 'spec_helper'
2
+
3
+ describe Atum::Generation::GeneratorResource do
4
+ let(:resource_schema) do
5
+ instance_double('Atum::Core::Schema::ResourceSchema',
6
+ name: 'super-awesome-resource',
7
+ description: 'really great',
8
+ link_schemas: [double, double, double])
9
+ end
10
+ let(:generator_resource) { described_class.new(resource_schema) }
11
+
12
+ describe '#name' do
13
+ subject { generator_resource.name }
14
+ it 'should delegate to the resource schema' do
15
+ expect(generator_resource.name).to eq('super_awesome_resource')
16
+ end
17
+ end
18
+
19
+ describe '#description' do
20
+ subject { generator_resource.description }
21
+ it { is_expected.to eq(resource_schema.description) }
22
+ end
23
+
24
+ describe '#links' do
25
+ subject(:method) { -> {} }
26
+
27
+ it 'should create a GeneratorLink for each link schema' do
28
+ expect(Atum::Generation::GeneratorLink).to receive(:new).exactly(3).times
29
+ generator_resource.links
30
+ end
31
+
32
+ it 'should return an Array of GeneratorLinks' do
33
+ generator_resource.links.each do |link|
34
+ expect(link).to be_a(Atum::Generation::GeneratorLink)
35
+ end
36
+ end
37
+
38
+ it 'should memoize' do
39
+ expect(resource_schema).to receive(:link_schemas).once
40
+ generator_resource.links
41
+ generator_resource.links
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+
3
+ describe Atum::Generation::GeneratorService do
4
+ let(:module_name) { 'Fruity' }
5
+ let(:schema_file) do
6
+ File.expand_path('../../fixtures/fruity_schema.json', File.dirname(__FILE__))
7
+ end
8
+ let(:url) { 'http://api.fruity.com' }
9
+ let(:options) { { path: File.join(tmp_folder, 'client') } }
10
+ let(:generator_service) do
11
+ described_class.new(module_name, schema_file, url, options)
12
+ end
13
+
14
+ def test_client_file(*args)
15
+ File.file?(File.join(options[:path], 'fruity', *args))
16
+ end
17
+
18
+ describe '#generate_files' do
19
+ let(:tmp_folder) do
20
+ File.expand_path(File.join('..', '..', '..', 'tmp'), File.dirname(__FILE__))
21
+ end
22
+ before { FileUtils.mkdir_p options[:path] }
23
+
24
+ it 'should create the required folders' do
25
+ generator_service.generate_files
26
+ expect(File.directory?(File.join(options[:path], 'fruity'))).to be_truthy
27
+ expect(File.directory?(File.join(options[:path], 'fruity/resources'))).to be_truthy
28
+ end
29
+
30
+ it 'should create a root client file' do
31
+ generator_service.generate_files
32
+ expect(File.file?(File.join(options[:path], 'fruity', 'client.rb'))).to be_truthy
33
+ end
34
+
35
+ it 'should create resource files' do
36
+ generator_service.generate_files
37
+ expect(test_client_file('resources', 'lemon.rb')).to be_truthy
38
+ expect(test_client_file('resources', 'lime.rb')).to be_truthy
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,75 @@
1
+ require 'spec_helper'
2
+
3
+ describe Atum::Generation::Generators::BaseGenerator do
4
+ let(:module_name) { 'Lemon' }
5
+ let(:schema) { instance_double('ApiSchema') }
6
+ let(:url) { 'http://api.lemon.com' }
7
+ let(:options) { {} }
8
+ let(:generator) { described_class.new(module_name, schema, url, options) }
9
+
10
+ describe '#context' do
11
+ let(:erb_context) { {} }
12
+
13
+ before do
14
+ allow(generator).to receive(:context_hash).and_return(erb_context)
15
+ end
16
+
17
+ it 'generates a new ErbContext' do
18
+ expect(Atum::Generation::ErbContext).to receive(:new)
19
+ generator.context
20
+ end
21
+
22
+ it 'sets the module name' do
23
+ expect(generator.context.to_hash.with_indifferent_access)
24
+ .to include(module_name: module_name)
25
+ end
26
+ end
27
+
28
+ describe '#template' do
29
+ context 'when TEMPLATE_NAME is not defined' do
30
+ subject(:method) { -> { generator.template } }
31
+ it { is_expected.to raise_error(NotImplementedError) }
32
+ end
33
+
34
+ context 'when TEMPLATE_NAME is defined' do
35
+ let(:template_name) { 'foobar' }
36
+ before do
37
+ allow(generator).to receive(:template_name).and_return(template_name)
38
+ end
39
+
40
+ it 'should attempt to read the file' do
41
+ expect(File).to receive(:read) do |arg|
42
+ expect(arg).to include(template_name)
43
+ end.and_return(nil)
44
+ generator.template
45
+ end
46
+
47
+ it 'should send the opened file through Erubis' do
48
+ file = double
49
+ allow(File).to receive(:read).and_return(file)
50
+ expect(Erubis::Eruby).to receive(:new).with(file)
51
+ generator.template
52
+ end
53
+ end
54
+ end
55
+
56
+ describe '#generate' do
57
+ it 'evaluates the template with the context' do
58
+ template = double
59
+ context = double
60
+ allow(generator).to receive(:template).and_return(template)
61
+ allow(generator).to receive(:context).and_return(context)
62
+ expect(template).to receive(:evaluate).with(context)
63
+ generator.generate
64
+ end
65
+ end
66
+
67
+ describe '#resources' do
68
+ it 'wraps resources with GeneratorResource objects' do
69
+ allow(schema).to receive(:resource_schemas).and_return([double])
70
+ generator.resources.each do |gr|
71
+ expect(gr).to be_a(Atum::Generation::GeneratorResource)
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ describe Atum::Generation::Generators::ClientGenerator do
4
+ let(:module_name) { 'Lemon' }
5
+ let(:schema) { double }
6
+ let(:url) { 'http://api.lemon.com' }
7
+ let(:options) { {} }
8
+ let(:generator) { described_class.new(module_name, schema, url, options) }
9
+
10
+ describe '#context' do
11
+ let(:erb_context) { {} }
12
+ let(:resources) { double }
13
+ let(:description) { double }
14
+ let(:schema) { double(description: description) }
15
+
16
+ before do
17
+ allow(generator).to receive(:resources).and_return(resources)
18
+ end
19
+
20
+ it 'sets the description on the context' do
21
+ expect(generator.context.to_hash.with_indifferent_access)
22
+ .to include(description: description)
23
+ end
24
+
25
+ it 'sets the resources on the context' do
26
+ expect(generator.context.to_hash.with_indifferent_access)
27
+ .to include(resources: resources)
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ describe Atum::Generation::Generators::ModuleGenerator do
4
+ let(:module_name) { 'Lemon' }
5
+ let(:schema) { instance_double('Atum::Schema::ApiSchema') }
6
+ let(:url) { 'http://api.lemon.com' }
7
+ let(:default_headers) { double }
8
+ let(:options) { { default_headers: default_headers } }
9
+ let(:generator) { described_class.new(module_name, schema, url, options) }
10
+
11
+ describe '#context' do
12
+ let(:erb_context) { {} }
13
+ let(:resources) { double }
14
+ let(:description) { double }
15
+ let(:schema) { double(description: description, schema: {}) }
16
+
17
+ before do
18
+ allow(generator).to receive(:resources).and_return(resources)
19
+ end
20
+
21
+ it 'sets the default_headers on the context' do
22
+ expect(generator.context.to_hash.with_indifferent_access)
23
+ .to include(default_headers: default_headers)
24
+ end
25
+
26
+ it 'sets the schema on the context' do
27
+ expect(generator.context.to_hash.with_indifferent_access)
28
+ .to include(schema: anything)
29
+ end
30
+
31
+ it 'sets the resources on the context' do
32
+ allow(generator).to receive(:resources).and_return(resources)
33
+ expect(generator.context.to_hash.with_indifferent_access)
34
+ .to include(resources: resources)
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+
3
+ describe Atum::Generation::Generators::ResourceGenerator do
4
+ let(:description) { double }
5
+ let(:class_name) { double }
6
+ let(:links) { double }
7
+ let(:resource_name) { double }
8
+ let(:resource) do
9
+ double(description: description,
10
+ class_name: class_name,
11
+ links: links,
12
+ name: resource_name)
13
+ end
14
+ let(:module_name) { 'Lemon' }
15
+ let(:schema) { double }
16
+ let(:url) { 'http://api.lemon.com' }
17
+ let(:default_headers) { double }
18
+ let(:options) { { default_headers: default_headers } }
19
+ let(:generator) do
20
+ described_class.new(resource, module_name, schema, url, options)
21
+ end
22
+
23
+ describe '#context' do
24
+ let(:erb_context) { {} }
25
+
26
+ it 'sets the description on the context' do
27
+ expect(generator.context.to_hash.with_indifferent_access)
28
+ .to include(description: description)
29
+ end
30
+
31
+ it 'sets the class_name on the context' do
32
+ expect(generator.context.to_hash.with_indifferent_access)
33
+ .to include(class_name: class_name)
34
+ end
35
+
36
+ it 'sets the links on the context' do
37
+ expect(generator.context.to_hash.with_indifferent_access)
38
+ .to include(links: links)
39
+ end
40
+
41
+ it 'sets the resource_name on the context' do
42
+ expect(generator.context.to_hash.with_indifferent_access)
43
+ .to include(resource_name: resource_name)
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ describe Atum::Generation::OptionsParameter do
4
+ let(:name) { 'options' }
5
+ let(:description) { 'any query parameters in the form of a hash' }
6
+ subject(:parameter) { described_class.new }
7
+
8
+ describe '#name' do
9
+ subject { parameter.name }
10
+ it { is_expected.to eq(name) }
11
+ end
12
+
13
+ describe '#description' do
14
+ subject { parameter.description }
15
+ it { is_expected.to eq(description) }
16
+ end
17
+
18
+ describe '#inspect' do
19
+ subject { parameter.inspect }
20
+ it { is_expected.to eq("Parameter(name=#{name}, description=#{description})") }
21
+ end
22
+
23
+ describe '#default' do
24
+ subject { parameter.default }
25
+ it { is_expected.to eq('{}') }
26
+ end
27
+ end
@@ -0,0 +1,161 @@
1
+ {
2
+ "description": "A schema for a Fruity API",
3
+ "definitions": {
4
+ "lemon": {
5
+ "description": "For lovers of lemons, look no further.",
6
+ "id": "schema/lemon",
7
+ "$schema": "http://json-schema.org/draft-04/hyper-schema",
8
+ "title": "Lemons",
9
+ "type": ["object"],
10
+ "definitions": {
11
+ "picked_on": {
12
+ "description": "The date on which the lemon was picked",
13
+ "example": "2013-10-19 22:10:29Z",
14
+ "format": "date-time",
15
+ "readOnly": true,
16
+ "type": ["string"]
17
+ },
18
+ "size": {
19
+ "description": "The size of the lemon, 'small', 'medium' or 'large'",
20
+ "example": "small",
21
+ "readOnly": true,
22
+ "enum": ["small", "medium", "large"]
23
+ },
24
+ "is_ripe": {
25
+ "description": "Whether or not the lemon is ripe",
26
+ "example": true,
27
+ "type": ["boolean"]
28
+ },
29
+ "lemon_uuid": {
30
+ "description": "A unique uuid reference for the lemon",
31
+ "example": "44724831-bf66-4bc2-865f-e2c4c2b14c78",
32
+ "format": "uuid",
33
+ "readOnly": true,
34
+ "type": ["string"]
35
+ },
36
+ "identity": {
37
+ "oneOf": [
38
+ { "$ref": "#/definitions/lemon/definitions/lemon_uuid" }
39
+ ]
40
+ }
41
+ },
42
+ "properties": {
43
+ "picked_on": { "$ref": "#/definitions/lemon/definitions/picked_on" },
44
+ "size": { "$ref": "#/definitions/lemon/definitions/size" },
45
+ "is_ripe": { "$ref": "#/definitions/lemon/definitions/is_ripe" },
46
+ "lemon_uuid": { "$ref": "#/definitions/lemon/definitions/lemon_uuid" }
47
+ },
48
+ "links": [
49
+ {
50
+ "description": "List all Lemons",
51
+ "href": "/lemon",
52
+ "method": "GET",
53
+ "rel": "instances",
54
+ "title": "List",
55
+ "schema": {
56
+ "type": ["object"],
57
+ "properties": {
58
+ "size": {
59
+ "type": ["array", "string"],
60
+ "items": { "$ref": "#/definitions/lemon/definitions/size" },
61
+ "maxItems": 2
62
+ }
63
+ },
64
+ "additionalProperties": false
65
+ }
66
+ },
67
+ {
68
+ "description": "Show a single Lemon",
69
+ "href": "/lemon/{(%23%2Fdefinitions%2Flemon%2Fdefinitions%2Flemon_uuid)}",
70
+ "method": "GET",
71
+ "rel": "self",
72
+ "title": "Info"
73
+ },
74
+ {
75
+ "description": "Create a new Lemon",
76
+ "href": "/lemon",
77
+ "method": "POST",
78
+ "rel": "create",
79
+ "title": "Create",
80
+ "schema": {
81
+ "properties": {
82
+ "picked_on": { "$ref": "#/definitions/lemon/definitions/picked_on" },
83
+ "size": { "$ref": "#/definitions/lemon/definitions/size" },
84
+ "is_ripe": { "$ref": "#/definitions/lemon/definitions/is_ripe" },
85
+ "lemon_uuid": { "$ref": "#/definitions/lemon/definitions/lemon_uuid" }
86
+ }
87
+ }
88
+ },
89
+ {
90
+ "description": "Update a sample lemon",
91
+ "href": "/lemon/{(%23%2Fdefinitions%2Flemon%2Fdefinitions%2Flemon_uuid)}",
92
+ "method": "PATCH",
93
+ "rel": "update",
94
+ "title": "Update",
95
+ "schema": {
96
+ "properties": {
97
+ "picked_on": { "$ref": "#/definitions/lemon/definitions/picked_on" },
98
+ "size": { "$ref": "#/definitions/lemon/definitions/size" },
99
+ "is_ripe": { "$ref": "#/definitions/lemon/definitions/is_ripe" },
100
+ "lemon_uuid": { "$ref": "#/definitions/lemon/definitions/lemon_uuid" }
101
+ }
102
+ }
103
+ },
104
+ {
105
+ "description": "Delete a whole size of lemons, you murderer",
106
+ "href": "/lemon/{(%23%2Fdefinitions%2Flemon%2Fdefinitions%2Fsize)}",
107
+ "method": "DELETE",
108
+ "rel": "destroy",
109
+ "title": "Delete"
110
+ }
111
+ ]
112
+ },
113
+ "lime": {
114
+ "description": "Like lemons, but not as good",
115
+ "id": "schema/lime",
116
+ "$schema": "http://json-schema.org/draft-04/hyper-schema",
117
+ "title": "Limes < Lemons",
118
+ "type": ["object"],
119
+ "definitions": {
120
+ "picked_on": {
121
+ "description": "The date on which the lime was picked",
122
+ "example": "2013-10-19 22:10:29Z",
123
+ "format": "date-time",
124
+ "readOnly": true,
125
+ "type": ["string"]
126
+ },
127
+ "lime_uuid": {
128
+ "description": "A unique uuid reference for the lime",
129
+ "example": "44724831-bf66-4bc2-865f-e2c4c2b14c78",
130
+ "format": "uuid",
131
+ "readOnly": true,
132
+ "type": ["string"]
133
+ },
134
+ "identity": {
135
+ "oneOf": [
136
+ { "$ref": "#/definitions/lime/definitions/lime_uuid" }
137
+ ]
138
+ }
139
+ },
140
+ "properties": {
141
+ "picked_on": { "$ref": "#/definitions/lime/definitions/picked_on" },
142
+ "lime_uuid": { "$ref": "#/definitions/lime/definitions/lime_uuid" }
143
+ },
144
+ "links": [
145
+ {
146
+ "description": "List all Limes",
147
+ "href": "/lime",
148
+ "method": "GET",
149
+ "rel": "instances",
150
+ "title": "List"
151
+ }
152
+ ]
153
+ }
154
+ },
155
+ "properties": {
156
+ "lemon": { "$ref": "#/definitions/lemon" },
157
+ "lime": { "$ref": "#/definitions/lime" }
158
+ }
159
+ }
160
+
161
+