svrless 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fa4ee241942179aafc8a3afdeb61575f273ca3602cf6ca00f3b3fceab4c7417b
4
- data.tar.gz: 1211b439d3251317f88183f6602318b10e192afc990006d88d9da04b1ef79203
3
+ metadata.gz: 87beb4ab1add30ff48878fb595f0fc4a9d4fbdb2a6e8cfb93bd74e86b11726ae
4
+ data.tar.gz: 278431e393d27ef935b22b52d5df8148c403118698af9a8dd0c37417313b9796
5
5
  SHA512:
6
- metadata.gz: ec5e5621c3878e06748773898bb2a181446e6d8235a96a0f949aae36415eec4fe37211f03e6a4b6e609f7f595291044deba3256dae8c58031714fce08be2d85e
7
- data.tar.gz: a01d89cf80dc8f6bdb65608af7148f446138f1247009bc715c46a4da85b401d2443aa5d297704d43abb4a587d0172c519b755c38c3bd91105254e5fba36b7c6d
6
+ metadata.gz: 7d1c9156e07022fe049ec8648edda5d3fc364db5056b5bd89a981d3c05352ab9fbe54c7606aaa7feaa0a9705749f45c50890bf261897f46dcece2ad0df8b7d3d
7
+ data.tar.gz: a9659b5bd8c304b0a0d07ea5a429e5b0a7e85697e8d2f8c583064df34adad0a6c0ab428695dcea7059feb342cb8cf3b97c260e6e4480cf0af2b742f9946d8bc3
data/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.2.0]- 2021-02-14
4
+
5
+ - Template generation is cleaner now.
6
+ - Replace Event with ResourceGenerator class.
7
+ - Moved template formation from ResourceGenerator into various generator classes.
8
+ - Added tests to cover these.
9
+
10
+ ## [0.1.1] - 2021-02-14
11
+
12
+ - Minor improvements to initial set up
13
+
3
14
  ## [0.1.0] - 2021-02-11
4
15
 
5
16
  - Initial release
data/README.md CHANGED
@@ -34,7 +34,7 @@ To create a new svrless project
34
34
 
35
35
  $ svrless
36
36
 
37
- # uncomment the posts resource in routes
37
+ # uncomment the resources in routes
38
38
 
39
39
  $ ruby config/routes.rb
40
40
 
@@ -46,6 +46,14 @@ To create a new svrless project
46
46
 
47
47
  $ make a POST request to http://localhost:3000/posts to see your lambda in action
48
48
 
49
+ ### Routes
50
+
51
+ The routes file accepts a list of resources in their singular form i.e
52
+
53
+ `resources :post, :comment, :user`
54
+
55
+ This will create controllers for those resources with routing mechanics in the SAM validated template.yaml.
56
+
49
57
  ## Development
50
58
 
51
59
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
data/bin/svrless CHANGED
@@ -25,9 +25,9 @@ File.open("config/routes.rb", "w") do |f|
25
25
  f.write "require 'svrless'\n"
26
26
  f.write "\n"
27
27
  f.write "class Routes\n"
28
- f.write " include Event\n"
28
+ f.write " include ResourceGenerator\n"
29
29
  f.write "\n"
30
- f.write " # resource :post\n"
30
+ f.write " # resources :post, :comment\n"
31
31
  f.write "end\n"
32
32
  end
33
33
 
data/lib/svrless.rb CHANGED
@@ -1,7 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/all"
4
+ require "yaml"
5
+
3
6
  require_relative "svrless/version"
4
- require_relative "svrless/event"
7
+ require_relative "svrless/resource_generator"
8
+ require_relative "svrless/generators/cloud_formation/main_generator"
5
9
 
6
10
  module Svrless
7
11
  class Error < StandardError; end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "svrless"
4
+
5
+ module CloudFormation
6
+ # Responsible for generating a hash which will eventually look like this:
7
+ # Create:
8
+ # Type: Api
9
+ # Properties:
10
+ # Method: post
11
+ # Path: "/posts"
12
+ #
13
+ # Input: klass: Class, action: :show
14
+ # Input: klass: Post, action: :update
15
+ class EventGenerator
16
+ attr_accessor :klass, :action
17
+
18
+ def initialize(klass:, action:)
19
+ @klass = klass
20
+ @action = action
21
+ end
22
+
23
+ def hash_representation
24
+ {
25
+ "#{@action}": {
26
+ type: "Api",
27
+ properties: {
28
+ method: http_method,
29
+ path: api_path
30
+ }
31
+ }
32
+ }
33
+ end
34
+
35
+ def http_method
36
+ case @action
37
+ when :create
38
+ "post"
39
+ when :show
40
+ "get"
41
+ when :update
42
+ "put"
43
+ else
44
+ "delete"
45
+ end
46
+ end
47
+
48
+ def api_path
49
+ return "/#{restify_klass_name}" if @action == :create
50
+
51
+ "/#{restify_klass_name}/{id}"
52
+ end
53
+
54
+ def restify_klass_name
55
+ @klass.to_s.underscore.pluralize.dasherize
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "svrless"
4
+
5
+ require_relative "event_generator"
6
+
7
+ module CloudFormation
8
+ # Responsible for generating a hash which will eventually look like this:
9
+ class FunctionGenerator
10
+ attr_accessor :klass, :actions
11
+
12
+ def initialize(klass:, actions:)
13
+ @klass = klass
14
+ @actions = actions
15
+ end
16
+
17
+ def hash_representation
18
+ {
19
+ "#{plural_klass_name}_controller": {
20
+ type: "AWS::Serverless::Function",
21
+ properties: properties_hash
22
+ }
23
+ }
24
+ end
25
+
26
+ private
27
+
28
+ def properties_hash
29
+ {
30
+ code_uri: "app/functions/#{plural_klass_name}",
31
+ events: events_hash,
32
+ function_name: "#{plural_klass_name}_controller".capitalize.camelize,
33
+ handler: "controller.#{klass_name}.router",
34
+ Layers: [{ Ref: "SvrlessLayer" }],
35
+ runtime: "ruby2.7"
36
+ }
37
+ end
38
+
39
+ def events_hash
40
+ hash = {}
41
+ @actions.each do |action|
42
+ hash.merge! EventGenerator.new(klass: @klass, action: action).hash_representation
43
+ end
44
+ hash
45
+ end
46
+
47
+ def plural_klass_name
48
+ @klass.to_s.pluralize.underscore
49
+ end
50
+
51
+ def klass_name
52
+ @klass.to_s.camelize.pluralize
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "svrless"
4
+
5
+ module CloudFormation
6
+ # Generates a hash representation of a AWS::Serverless::LayerVersion
7
+ class LayerGenerator
8
+ # TODO: allow naming overrides via initialize
9
+
10
+ def hash_representation
11
+ {
12
+ svrless_layer: {
13
+ type: "AWS::Serverless::LayerVersion",
14
+ properties: hash_properties
15
+ }
16
+ }
17
+ end
18
+
19
+ private
20
+
21
+ def hash_properties
22
+ {
23
+ layer_name: "svrless-shared-layer",
24
+ description: "SVRLESS shared logic",
25
+ content_uri: "app/shared/.",
26
+ compatible_runtimes: ["ruby2.7"]
27
+ }
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "svrless"
4
+ require_relative "layer_generator"
5
+ require_relative "function_generator"
6
+
7
+ module CloudFormation
8
+ # The main outer shell of SAM template generators
9
+ class MainGenerator
10
+ AWS_TEMPLATE_VERSION = "2010-09-09"
11
+ AWS_TRANSFORM_VERSION = "AWS::Serverless-2016-10-31"
12
+
13
+ attr_accessor :klasses, :actions
14
+
15
+ def initialize(klasses:, actions:)
16
+ @klasses = klasses
17
+ @actions = actions
18
+ end
19
+
20
+ def representation
21
+ main_template_hash.deep_transform_keys { |k| k.to_s.capitalize.camelize }.to_yaml
22
+ end
23
+
24
+ def hash_representation
25
+ main_template_hash
26
+ end
27
+
28
+ private
29
+
30
+ def main_template_hash
31
+ {
32
+ a_w_s_template_format_version: AWS_TEMPLATE_VERSION,
33
+ transform: AWS_TRANSFORM_VERSION,
34
+ resources: resources_hash
35
+ }
36
+ end
37
+
38
+ def resources_hash
39
+ resources = {}
40
+ resources.merge! function_hashes
41
+ resources.merge! CloudFormation::LayerGenerator.new.hash_representation
42
+ resources
43
+ end
44
+
45
+ def function_hashes
46
+ functions = {}
47
+ @klasses.each do |klass|
48
+ functions.merge! CloudFormation::FunctionGenerator.new(klass: klass, actions: actions).hash_representation
49
+ end
50
+ functions
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "svrless"
4
+
5
+ module ResourceGenerator
6
+ module ClassMethods
7
+ DEFAULT_ACTIONS = %i[create show update destroy].freeze
8
+
9
+ def resources(*args)
10
+ generate_svrless_controller
11
+ generate_cloudformation(klasses: args, actions: DEFAULT_ACTIONS)
12
+ generate_function_files(klasses: args, actions: DEFAULT_ACTIONS)
13
+ end
14
+
15
+ def generate_cloudformation(klasses:, actions:)
16
+ output = File.open("template.yaml", "w")
17
+ output << CloudFormation::MainGenerator.new(klasses: klasses, actions: actions).representation
18
+ output.close
19
+ end
20
+
21
+ def plural_klass_name(klass)
22
+ klass.to_s.pluralize
23
+ end
24
+
25
+ def full_klass_name(klass)
26
+ klass.to_s.camelize.pluralize
27
+ end
28
+
29
+ def generate_function_files(klasses:, actions:)
30
+ klasses.each do |klass|
31
+ generate_function_file(klass: klass, actions: actions)
32
+ end
33
+ end
34
+
35
+ def generate_function_file(klass:, actions:)
36
+ Dir.mkdir("app") unless Dir.exist?("app")
37
+ Dir.mkdir("app/functions") unless Dir.exist?("app/functions")
38
+ unless Dir.exist?("app/functions/#{plural_klass_name(klass)}")
39
+ Dir.mkdir("app/functions/#{plural_klass_name(klass)}")
40
+ end
41
+ FileUtils.touch("app/functions/#{plural_klass_name(klass)}/controller.rb")
42
+ File.open("app/functions/#{plural_klass_name(klass)}/controller.rb", "w") do |f|
43
+ f.write "require 'svrless_controller'"
44
+ f.write "\n"
45
+ f.write "\n"
46
+ f.write "class #{full_klass_name(klass)} < SvrlessController"
47
+ f.write "\n"
48
+ actions.each_with_index do |method, index|
49
+ f.write " def self.#{method}(event:, context:)\n"
50
+ f.write " {\n"
51
+ f.write " statusCode: 200,\n"
52
+ f.write " body: { hello: 'world' }.to_json\n"
53
+ f.write " }\n"
54
+ f.write " end\n"
55
+ f.write "\n" unless actions.size == index + 1
56
+ end
57
+ f.write "end"
58
+ f.write "\n"
59
+ end
60
+ end
61
+
62
+ def generate_svrless_controller
63
+ Dir.mkdir("app") unless Dir.exist?("app")
64
+ Dir.mkdir("app/shared") unless Dir.exist?("app/shared")
65
+ Dir.mkdir("app/shared/ruby") unless Dir.exist?("app/shared/ruby")
66
+ Dir.mkdir("app/shared/ruby/lib") unless Dir.exist?("app/shared/ruby/lib")
67
+ File.open("app/shared/ruby/lib/svrless_controller.rb", "w") do |f|
68
+ f.write "class SvrlessController\n"
69
+ f.write " def self.router(event:, context:)\n"
70
+ f.write " case event['httpMethod']\n"
71
+ f.write " when 'GET'\n"
72
+ f.write " show(event: event, context: context)\n"
73
+ f.write " when 'POST'\n"
74
+ f.write " create(event: event, context: context)\n"
75
+ f.write " when 'DELETE'\n"
76
+ f.write " destroy(event: event, context: context)\n"
77
+ f.write " else\n"
78
+ f.write " update(event: event, context: context)\n"
79
+ f.write " end\n"
80
+ f.write " end\n"
81
+ f.write "end\n"
82
+ end
83
+ end
84
+ end
85
+
86
+ def self.included(klass)
87
+ klass.extend ClassMethods
88
+ end
89
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Svrless
4
- VERSION = "0.1.1"
4
+ VERSION = "0.2.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: svrless
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - zrthko
@@ -45,7 +45,11 @@ files:
45
45
  - bin/setup
46
46
  - bin/svrless
47
47
  - lib/svrless.rb
48
- - lib/svrless/event.rb
48
+ - lib/svrless/generators/cloud_formation/event_generator.rb
49
+ - lib/svrless/generators/cloud_formation/function_generator.rb
50
+ - lib/svrless/generators/cloud_formation/layer_generator.rb
51
+ - lib/svrless/generators/cloud_formation/main_generator.rb
52
+ - lib/svrless/resource_generator.rb
49
53
  - lib/svrless/version.rb
50
54
  - svrless.gemspec
51
55
  homepage: https://github.com/svrless/svrless
data/lib/svrless/event.rb DELETED
@@ -1,179 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "active_support/all"
4
- require "yaml"
5
-
6
- module Event
7
- module ClassMethods
8
- def events(*args, resource)
9
- raise StandardError, "Event: You need to supply at least one argument" if args.empty?
10
-
11
- events = args
12
- generate(events, resource)
13
- end
14
-
15
- def resource(args)
16
- resource = args
17
- events(:create, :show, :update, :destroy, resource)
18
- generate_function_files(:create, :show, :update, :destroy, resource)
19
- generate_svrless_controller
20
- end
21
-
22
- def generate(events, resource)
23
- puts "Generating resource..."
24
- # make_function_files
25
- output = File.open("template.yaml", "w")
26
- output << main_representation(events, resource)
27
- output.close
28
- puts "DONE!"
29
- end
30
-
31
- def main_representation(events, resource)
32
- {
33
- a_w_s_template_format_version: "2010-09-09",
34
- transform: "AWS::Serverless-2016-10-31",
35
- resources: resources_hash(events, resource)
36
- }.deep_transform_keys { |k| k.to_s.capitalize.camelize }.to_yaml
37
- end
38
-
39
- def resources_hash(events, resource)
40
- layer.merge(function_hash(events, resource))
41
- end
42
-
43
- def layer
44
- {
45
- svrless_layer: {
46
- type: "AWS::Serverless::LayerVersion",
47
- properties: {
48
- layer_name: "svrless-layer",
49
- description: "All the business logic should go in here",
50
- content_uri: "app/shared/.",
51
- compatible_runtimes: ["ruby2.7"]
52
- }
53
- }
54
- }
55
- end
56
-
57
- def function_hash(events, resource)
58
- {
59
- "#{plural_classname(resource)}_controller": {
60
- type: "AWS::Serverless::Function",
61
- properties: {
62
- code_uri: "app/functions/#{plural_classname(resource)}",
63
- events: hash_of_events(events, resource),
64
- function_name: "#{plural_classname(resource)}_controller".capitalize.camelize,
65
- handler: "controller.#{full_classname(resource)}.router",
66
- Layers: [{ Ref: "SvrlessLayer" }],
67
- runtime: "ruby2.7"
68
- }
69
- }
70
- }
71
- end
72
-
73
- def hash_of_events(events, resource)
74
- h = {}
75
- events.each do |event|
76
- h.merge!(
77
- {
78
- "#{event}": {
79
- type: "Api",
80
- properties: {
81
- method: method_from_event(event: event),
82
- path: path_from_event(event: event, resource: resource)
83
- }
84
- }
85
- }
86
- )
87
- end
88
- h
89
- end
90
-
91
- def plural_classname(resource)
92
- resource.to_s.pluralize
93
- end
94
-
95
- def method_from_event(event:)
96
- case event
97
- when :create
98
- "post"
99
- when :show
100
- "get"
101
- when :update
102
- "put"
103
- when :destroy
104
- "delete"
105
- end
106
- end
107
-
108
- def path_from_event(event:, resource:)
109
- case event
110
- when :create
111
- "/#{plural_classname(resource)}"
112
- when :show
113
- "/#{plural_classname(resource)}/{id}"
114
- when :update
115
- "/#{plural_classname(resource)}/{id}"
116
- when :destroy
117
- "/#{plural_classname(resource)}/{id}"
118
- end
119
- end
120
-
121
- def full_classname(resource)
122
- "#{plural_classname(resource)}_controller".camelize
123
- end
124
-
125
- def generate_function_files(*args, resource)
126
- Dir.mkdir("app") unless Dir.exist?("app")
127
- Dir.mkdir("app/functions") unless Dir.exist?("app/functions")
128
- unless Dir.exist?("app/functions/#{plural_classname(resource)}")
129
- Dir.mkdir("app/functions/#{plural_classname(resource)}")
130
- end
131
- FileUtils.touch("app/functions/#{plural_classname(resource)}/controller.rb")
132
- File.open("app/functions/#{plural_classname(resource)}/controller.rb", "w") do |f|
133
- f.write "require 'svrless_controller'"
134
- f.write "\n"
135
- f.write "\n"
136
- f.write "class #{full_classname(resource)} < SvrlessController"
137
- f.write "\n"
138
- args.each_with_index do |method, index|
139
- f.write " def self.#{method}(event:, context:)\n"
140
- f.write " {\n"
141
- f.write " statusCode: 200,\n"
142
- f.write " body: { hello: 'world' }.to_json\n"
143
- f.write " }\n"
144
- f.write " end\n"
145
- f.write "\n" unless args.size == index + 1
146
- end
147
- f.write "end"
148
- f.write "\n"
149
- end
150
- end
151
-
152
- def generate_svrless_controller
153
- Dir.mkdir("app") unless Dir.exist?("app")
154
- Dir.mkdir("app/shared") unless Dir.exist?("app/shared")
155
- Dir.mkdir("app/shared/ruby") unless Dir.exist?("app/shared/ruby")
156
- Dir.mkdir("app/shared/ruby/lib") unless Dir.exist?("app/shared/ruby/lib")
157
- File.open("app/shared/ruby/lib/svrless_controller.rb", "w") do |f|
158
- f.write "class SvrlessController\n"
159
- f.write " def self.router(event:, context:)\n"
160
- f.write " case event['httpMethod']\n"
161
- f.write " when 'GET'\n"
162
- f.write " show(event: event, context: context)\n"
163
- f.write " when 'POST'\n"
164
- f.write " create(event: event, context: context)\n"
165
- f.write " when 'DELETE'\n"
166
- f.write " destroy(event: event, context: context)\n"
167
- f.write " else\n"
168
- f.write " update(event: event, context: context)\n"
169
- f.write " end\n"
170
- f.write " end\n"
171
- f.write "end\n"
172
- end
173
- end
174
- end
175
-
176
- def self.included(klass)
177
- klass.extend ClassMethods
178
- end
179
- end