svrless 0.1.1 → 0.2.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 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