jets 1.7.2 → 1.8.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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -0
  3. data/README.md +1 -1
  4. data/jets.gemspec +2 -0
  5. data/lib/jets.rb +1 -0
  6. data/lib/jets/application.rb +45 -21
  7. data/lib/jets/aws_services.rb +2 -0
  8. data/lib/jets/aws_services/s3_bucket.rb +26 -0
  9. data/lib/jets/booter.rb +82 -2
  10. data/lib/jets/builders/code_builder.rb +20 -7
  11. data/lib/jets/builders/handler_generator.rb +20 -6
  12. data/lib/jets/cfn/builders/base_child_builder.rb +5 -17
  13. data/lib/jets/cfn/builders/parent_builder.rb +1 -1
  14. data/lib/jets/commands/build.rb +6 -0
  15. data/lib/jets/commands/deploy.rb +10 -0
  16. data/lib/jets/commands/templates/skeleton/config/environments/development.rb +4 -1
  17. data/lib/jets/commands/templates/skeleton/config/environments/production.rb +6 -1
  18. data/lib/jets/commands/templates/skeleton/config/environments/test.rb +7 -0
  19. data/lib/jets/controller/base.rb +1 -2
  20. data/lib/jets/controller/rendering/rack_renderer.rb +11 -3
  21. data/lib/jets/core.rb +5 -72
  22. data/lib/jets/internal/app/controllers/jets/mailers_controller.rb +97 -0
  23. data/lib/jets/internal/app/helpers/jets/mailers_helper.rb +9 -0
  24. data/lib/jets/internal/app/shared/functions/jets/s3_bucket_config.rb +43 -0
  25. data/lib/jets/internal/app/views/jets/mailers/email.html.erb +145 -0
  26. data/lib/jets/internal/app/views/jets/mailers/index.html.erb +8 -0
  27. data/lib/jets/internal/app/views/jets/mailers/mailer.html.erb +6 -0
  28. data/lib/jets/job/base.rb +10 -0
  29. data/lib/jets/job/dsl.rb +2 -0
  30. data/lib/jets/job/dsl/s3_event.rb +36 -0
  31. data/lib/jets/job/dsl/sns_event.rb +8 -0
  32. data/lib/jets/job/s3_event_helper.rb +13 -0
  33. data/lib/jets/lambda/dsl.rb +11 -1
  34. data/lib/jets/mailer.rb +51 -0
  35. data/lib/jets/resource/child_stack/app_class.rb +6 -15
  36. data/lib/jets/resource/child_stack/shared.rb +3 -1
  37. data/lib/jets/resource/events/rule.rb +1 -1
  38. data/lib/jets/resource/lambda/event_source_mapping.rb +1 -1
  39. data/lib/jets/resource/permission.rb +1 -1
  40. data/lib/jets/resource/replacer.rb +8 -0
  41. data/lib/jets/resource/s3.rb +3 -17
  42. data/lib/jets/resource/s3/bucket.rb +24 -0
  43. data/lib/jets/resource/sns.rb +1 -0
  44. data/lib/jets/resource/sns/subscription.rb +1 -1
  45. data/lib/jets/resource/sns/topic.rb +1 -1
  46. data/lib/jets/resource/sns/topic_policy.rb +40 -0
  47. data/lib/jets/resource/sqs/queue.rb +1 -1
  48. data/lib/jets/stack.rb +19 -3
  49. data/lib/jets/stack/builder.rb +6 -1
  50. data/lib/jets/stack/depends.rb +36 -0
  51. data/lib/jets/stack/depends/item.rb +9 -0
  52. data/lib/jets/stack/function.rb +19 -10
  53. data/lib/jets/stack/main/dsl.rb +4 -0
  54. data/lib/jets/stack/main/extensions/iam.rb +8 -0
  55. data/lib/jets/stack/main/extensions/lambda.rb +20 -7
  56. data/lib/jets/stack/main/extensions/s3.rb +12 -0
  57. data/lib/jets/stack/main/extensions/sns.rb +4 -0
  58. data/lib/jets/stack/s3_event.rb +87 -0
  59. data/lib/jets/turbine.rb +11 -0
  60. data/lib/jets/version.rb +1 -1
  61. metadata +48 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3d4b8b6547644c895f4b9f86ea9c58d1732f89bd68cc3f71a73cf4d2a6733eb0
4
- data.tar.gz: e6447ef5b8099c9c405be40315cf9aa2dc711958158a79a86f578c1f0258117d
3
+ metadata.gz: 22ccf0e1b4f1fe6841677f5e80a47d28420176db2b89d0e66852d71841feac32
4
+ data.tar.gz: 0dff68b46ad8f254a1fc8ffe71d95d52b58cc21b1ccd4cf78ec0e62c212d4ed6
5
5
  SHA512:
6
- metadata.gz: 5a90c4010311c435bc32714738dd83ece60822a8c34cb9d848c198bd0d8d9518129a764c5edf3ea873747f3ff38d3630ec991a907f5afd9acf6befabada4fd58
7
- data.tar.gz: b2013f481f5be249ee7798bb746306d25e52ff9f1515020b28c3e7e7583511ea3796666734a202c3514235d0d6816ce28e677ef397238c88531a44116f0cbbb7
6
+ metadata.gz: 3facb23a28890cea9a9627808198e7499975ad1874ffb4371fa14a757ff45b3f74e1daa853d51ca966c170450ee7e2b0d6e48a3dea621aad62936cf8f1b98312
7
+ data.tar.gz: 46369c376727ebc86de017043ff079abbd077f2a1c090193ad0cebd728528e3e2794df003b1d9c8667cd333dec8880673cb92e60d8eeee929790f88c4f3978c5
data/CHANGELOG.md CHANGED
@@ -3,6 +3,12 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  This project *loosely tries* to adhere to [Semantic Versioning](http://semver.org/), even before v1.0.
5
5
 
6
+ ## [1.8.0]
7
+ - #191 Email Support via ActionMailer
8
+ - #192 S3 Event Support
9
+ - Turbine after_initializer
10
+ - Improve Jets.boot ordering
11
+
6
12
  ## [1.7.2]
7
13
  - #189: spec_helpers: `get` request now converts dangling params to query params. `query` keyword can be used to do the same thing explicitly.
8
14
  - #190 SNS Event Lambda Trigger Support
data/README.md CHANGED
@@ -31,7 +31,7 @@ Jets supports writing AWS Lambda functions with Ruby. You define them in the `ap
31
31
  app/functions/simple.rb:
32
32
 
33
33
  ```ruby
34
- def handler_function(event:, context:)
34
+ def lambda_handler(event:, context:)
35
35
  puts "hello world"
36
36
  {hello: "world"}
37
37
  end
data/jets.gemspec CHANGED
@@ -29,6 +29,7 @@ Gem::Specification.new do |spec|
29
29
 
30
30
  # spec.add_dependency "actionpack", "~> 5.2.1" # using vendor/rails version
31
31
  # spec.add_dependency "actionview", "~> 5.2.1" # using vendor/rails version
32
+ spec.add_dependency "actionmailer", "~> 5.2.1"
32
33
  spec.add_dependency "activerecord", "~> 5.2.1"
33
34
  spec.add_dependency "activesupport", "~> 5.2.1"
34
35
  spec.add_dependency "aws-sdk-apigateway"
@@ -39,6 +40,7 @@ Gem::Specification.new do |spec|
39
40
  spec.add_dependency "aws-sdk-s3"
40
41
  spec.add_dependency "aws-sdk-sns"
41
42
  spec.add_dependency "aws-sdk-sqs"
43
+ spec.add_dependency "cfnresponse"
42
44
  spec.add_dependency "dotenv"
43
45
  spec.add_dependency "gems" # jets-gems dependency
44
46
  spec.add_dependency "hashie"
data/lib/jets.rb CHANGED
@@ -36,6 +36,7 @@ module Jets
36
36
  autoload :Klass, 'jets/klass'
37
37
  autoload :Lambda, 'jets/lambda'
38
38
  autoload :Logger, "jets/logger"
39
+ autoload :Mailer, "jets/mailer"
39
40
  autoload :Mega, "jets/mega"
40
41
  autoload :Middleware, "jets/middleware"
41
42
  autoload :Naming, 'jets/naming'
@@ -12,13 +12,33 @@ class Jets::Application
12
12
  end
13
13
 
14
14
  def setup!
15
- load_configs
15
+ load_default_config
16
+ setup_auto_load_paths
17
+ end
18
+
19
+ def configs!
20
+ load_environments_config
21
+ load_db_config
22
+ set_iam_policy # relies on dependent values, must be called afterwards
23
+ normalize_env_vars!
24
+ end
25
+
26
+ # After the mimimal template gets build, we need to reload it for the full stack
27
+ # creation. This allows us to reference IAM policies configs that depend on the
28
+ # creation of the s3 bucket.
29
+ def reload_configs!
30
+ # Tricky: reset only the things that depends on the minimal stack
31
+ @config.iam_policy = nil
32
+ configs!
16
33
  end
17
34
 
18
35
  def finish!
36
+ deprecated_configs_message
19
37
  load_inflections
20
- setup_auto_load_paths
21
38
  load_routes
39
+ # Load libraries at the end to trigger onload so we can defined options in any order.
40
+ # Only action_mailer library have been used properly this way so far.
41
+ require 'action_mailer'
22
42
  end
23
43
 
24
44
  def load_inflections
@@ -98,6 +118,26 @@ class Jets::Application
98
118
  config.encoding = ActiveSupport::OrderedOptions.new
99
119
  config.encoding.default = "utf-8"
100
120
 
121
+ config.s3_event = ActiveSupport::OrderedOptions.new
122
+ # These notification_configuration properties correspond to the ruby aws-sdk
123
+ # s3.put_bucket_notification_configuration
124
+ # in jets/s3_bucket_config.rb, not the CloudFormation Bucket properties. The CloudFormation
125
+ # bucket properties have a similiar structure but is slightly different so it can be confusing.
126
+ #
127
+ # Ruby aws-sdk S3 Docs: https://amzn.to/2N7m5Lr
128
+ config.s3_event.configure_bucket = true
129
+ config.s3_event.notification_configuration = {
130
+ topic_configurations: [
131
+ {
132
+ events: ["s3:ObjectCreated:*"],
133
+ topic_arn: "!Ref SnsTopic", # must use this logical id
134
+ },
135
+ ],
136
+ }
137
+
138
+ # So tried to defined this in the jets/mailer.rb Turbine only but jets new requires it
139
+ # config.action_mailer = ActiveSupport::OrderedOptions.new
140
+
101
141
  config
102
142
  end
103
143
 
@@ -116,14 +156,11 @@ class Jets::Application
116
156
  project_name_line.gsub(/.*=/,'').strip.gsub(/["']/,'') # project_name
117
157
  end
118
158
 
119
- def load_app_config
159
+ def load_default_config
120
160
  @config = default_config
121
161
  set_dependent_configs! # things like project_namespace that need project_name
122
162
  eval_app_config # this overwrites Jets.config.project_name
123
163
  Jets.config.project_name = parse_project_name # Must set again because JETS_PROJECT_NAME is possible
124
-
125
- set_iam_policy # relies on dependent values, must be called afterwards
126
- normalize_env_vars!
127
164
  end
128
165
 
129
166
  def eval_app_config
@@ -131,13 +168,6 @@ class Jets::Application
131
168
  load app_config # use load instead of require so reload_configs! works
132
169
  end
133
170
 
134
- # After the mimimal template gets build, we need to reload it for the full stack
135
- # creation. This allows us to reference IAM policies configs that depend on the
136
- # creation of the s3 bucket.
137
- def reload_configs!
138
- load_configs
139
- end
140
-
141
171
  def load_environments_config
142
172
  env_file = "#{Jets.root}/config/environments/#{Jets.env}.rb"
143
173
  if File.exist?(env_file)
@@ -146,13 +176,6 @@ class Jets::Application
146
176
  end
147
177
  end
148
178
 
149
- def load_configs
150
- load_app_config
151
- load_db_config
152
- load_environments_config
153
- deprecated_configs_message
154
- end
155
-
156
179
  def deprecated_configs_message
157
180
  unless config.ruby.lazy_load.nil?
158
181
  puts "Detected config.ruby.lazy_load = #{config.ruby.lazy_load.inspect}".color(:yellow)
@@ -195,8 +218,9 @@ class Jets::Application
195
218
  internal = File.expand_path("../internal", __FILE__)
196
219
  paths = %w[
197
220
  app/controllers
198
- app/models
221
+ app/helpers
199
222
  app/jobs
223
+ app/models
200
224
  ]
201
225
  paths.map { |path| "#{internal}/#{path}" }
202
226
  end
@@ -10,6 +10,8 @@ require "aws-sdk-sqs"
10
10
 
11
11
  module Jets::AwsServices
12
12
  autoload :StackStatus, 'jets/aws_services/stack_status'
13
+ autoload :S3Bucket, 'jets/aws_services/s3_bucket'
14
+
13
15
  include StackStatus
14
16
  extend Memoist
15
17
 
@@ -0,0 +1,26 @@
1
+ module Jets::AwsServices
2
+ class S3Bucket
3
+ include Jets::AwsServices
4
+
5
+ def self.ensure_exists(bucket_name)
6
+ new(bucket_name).ensure_exists
7
+ end
8
+
9
+ def initialize(name)
10
+ @name = name
11
+ end
12
+
13
+ def ensure_exists
14
+ s3.create_bucket(bucket: @name) unless exists?
15
+ end
16
+
17
+ def exists?
18
+ begin
19
+ s3.head_bucket(bucket: @name)
20
+ true
21
+ rescue
22
+ false
23
+ end
24
+ end
25
+ end
26
+ end
data/lib/jets/booter.rb CHANGED
@@ -10,11 +10,19 @@ class Jets::Booter
10
10
  Jets::Dotenv.load!
11
11
 
12
12
  Jets.application.setup!
13
+ # eager_load_jets is called to ensure that internal Turbines get loaded after auto_load paths configured in setup!
14
+ eager_load_jets
15
+ run_turbines(:initializers)
16
+ # Load configs after Turbine initializers so Turbines can defined some config options
17
+ # and they are available in user's project environment configs.
18
+ Jets.application.configs!
13
19
  app_initializers
14
- turbine_initializers
20
+ run_turbines(:after_initializers)
15
21
  Jets.application.finish!
16
22
 
17
- Jets.eager_load!
23
+ # Eager load project code. Rather have user find out early than late.
24
+ eager_load_app
25
+
18
26
  setup_db
19
27
  # build_middleware_stack # TODO: figure out how to build middleware during Jets.boot without breaking jets new and webpacker:install
20
28
 
@@ -51,6 +59,17 @@ class Jets::Booter
51
59
  end
52
60
  end
53
61
 
62
+ # run_turbines(:initializers)
63
+ # run_turbines(:after_initializers)
64
+ def run_turbines(name)
65
+ Jets::Turbine.subclasses.each do |subclass|
66
+ hooks = subclass.send(name) || []
67
+ hooks.each do |label, block|
68
+ block.call(Jets.application)
69
+ end
70
+ end
71
+ end
72
+
54
73
  # require_bundle_gems called when environment boots up via Jets.boot. It
55
74
  # also useful for when to loading Rake tasks in
56
75
  # Jets::Commands::RakeTasks.load!
@@ -127,5 +146,66 @@ class Jets::Booter
127
146
  exit 1
128
147
  end
129
148
  end
149
+
150
+ # Eager load jet's lib and classes
151
+ def eager_load_jets
152
+ lib_jets = File.expand_path(".", File.dirname(__FILE__))
153
+ Dir.glob("#{lib_jets}/**/*.rb").select do |path|
154
+ next if !File.file?(path)
155
+ next if skip_eager_load_paths?(path)
156
+
157
+ path = path.sub("#{lib_jets}/","jets/")
158
+ class_name = path
159
+ .sub(/\.rb$/,'') # remove .rb
160
+ .sub(/^\.\//,'') # remove ./
161
+ .sub(/app\/\w+\//,'') # remove app/controllers or app/jobs etc
162
+ .camelize
163
+ # special class mappings
164
+ class_name = class_mappings(class_name)
165
+ class_name.constantize # use constantize instead of require so dont have to worry about order.
166
+ end
167
+ end
168
+
169
+ # Skip these paths because eager loading doesnt work for them.
170
+ # Scope to /jets as much as possible in case it collides with a user path
171
+ def skip_eager_load_paths?(path)
172
+ path =~ %r{/jets/builders/rackup_wrappers} ||
173
+ path =~ %r{/jets/builders/reconfigure_rails} ||
174
+ path =~ %r{/jets/cli} ||
175
+ path =~ %r{/jets/commands/templates/webpacker} ||
176
+ path =~ %r{/jets/controller/middleware/webpacker_setup} ||
177
+ path =~ %r{/jets/core_ext} ||
178
+ path =~ %r{/jets/internal/app} ||
179
+ path =~ %r{/jets/overrides} ||
180
+ path =~ %r{/jets/spec} ||
181
+ path =~ %r{/jets/stack} ||
182
+ path =~ %r{/jets/turbo/project/} ||
183
+ path =~ %r{/jets/version} ||
184
+ path =~ %r{/templates/}
185
+ end
186
+
187
+ def class_mappings(class_name)
188
+ map = {
189
+ "Jets::Io" => "Jets::IO",
190
+ }
191
+ map[class_name] || class_name
192
+ end
193
+
194
+ # Eager load user's application
195
+ def eager_load_app
196
+ Dir.glob("#{Jets.root}/app/**/*.rb").select do |path|
197
+ next if !File.file?(path) or path =~ %r{/javascript/} or path =~ %r{/views/}
198
+ next if path.include?('app/functions') || path.include?('app/shared/functions') || path.include?('app/internal/functions')
199
+
200
+ class_name = path
201
+ .sub(/\.rb$/,'') # remove .rb
202
+ .sub(%{^\./},'') # remove ./
203
+ .sub("#{Jets.root}/",'')
204
+ .sub(%r{app/shared/\w+/},'') # remove shared/resources or shared/extensions
205
+ .sub(%r{app/\w+/},'') # remove app/controllers or app/jobs etc
206
+ class_name = class_name.classify
207
+ class_name.constantize # use constantize instead of require so dont have to worry about order.
208
+ end
209
+ end
130
210
  end
131
211
  end
@@ -53,7 +53,7 @@ class Jets::Builders
53
53
  Md5.compute! # populates Md5.checksums hash
54
54
  end
55
55
 
56
- def generate_node_shims
56
+ def generate_shims
57
57
  headline "Generating shims in the handlers folder."
58
58
  # Crucial that the Dir.pwd is in the tmp_code because for
59
59
  # Jets::Builders::app_files because Jets.boot set ups
@@ -97,8 +97,8 @@ class Jets::Builders
97
97
 
98
98
  # Code prep and zipping
99
99
  check_code_size!
100
- calculate_md5s # must be called before generate_node_shims and create_zip_files
101
- generate_node_shims
100
+ calculate_md5s # must be called before generate_shims and create_zip_files
101
+ generate_shims
102
102
  create_zip_files
103
103
  end
104
104
 
@@ -106,16 +106,29 @@ class Jets::Builders
106
106
  CodeSize.check!
107
107
  end
108
108
 
109
- # We copy the files into the project because we cannot require simple functions
110
- # directly since they are wrapped by an anonymous class.
111
- # TODO: Do this with the other files we required the same way.
109
+ # Materialized internal code into actually user Jets app as part of the deploy process.
110
+ # Examples of things that we might materialize:
111
+ #
112
+ # Views
113
+ # Simple Functions
114
+ #
115
+ # For functions, We copy the files into the project because we cannot require
116
+ # simple functions directly since they are wrapped by an anonymous class.
112
117
  def copy_internal_jets_code
113
118
  files = []
119
+
120
+ mailers_controller = Jets::Router.has_controller?("Jets::MailersController")
121
+ if mailers_controller
122
+ files << "app/controllers/jets/mailers_controller.rb"
123
+ files << "app/views/jets/mailers"
124
+ files << "app/helpers/jets/mailers_helper.rb"
125
+ end
126
+
114
127
  files.each do |relative_path|
115
128
  src = File.expand_path("../internal/#{relative_path}", File.dirname(__FILE__))
116
129
  dest = "#{"#{stage_area}/code"}/#{relative_path}"
117
130
  FileUtils.mkdir_p(File.dirname(dest))
118
- FileUtils.cp(src, dest)
131
+ FileUtils.cp_r(src, dest)
119
132
  end
120
133
  end
121
134
 
@@ -96,20 +96,31 @@ class Jets::Builders
96
96
 
97
97
  def internal_shims
98
98
  jets_base_path if Jets.custom_domain?
99
+ s3_bucket_config if Jets.s3_event?
99
100
  end
100
101
 
101
102
  def jets_base_path
102
- path = "jets/base_path.rb"
103
+ copy_function_template("functions/jets/base_path.rb", stage_name: Jets::Resource::ApiGateway::Deployment.stage_name)
104
+ end
105
+
106
+ def s3_bucket_config
107
+ copy_function_template("shared/functions/jets/s3_bucket_config.rb")
108
+ end
109
+
110
+ # Copy code from internal folder to materialized app code
111
+ def copy_function_template(path, vars={})
103
112
  internal = File.expand_path("../internal", File.dirname(__FILE__))
104
- src = "#{internal}/app/functions/#{path}"
105
- result = Jets::Erb.result(src, stage_name: Jets::Resource::ApiGateway::Deployment.stage_name)
106
- dest = "#{tmp_code}/handlers/functions/#{path}"
113
+ src = "#{internal}/app/#{path}"
114
+ result = Jets::Erb.result(src, vars)
115
+ dest = "#{tmp_code}/handlers/#{path}"
107
116
  FileUtils.mkdir_p(File.dirname(dest))
108
117
  IO.write(dest, result)
109
118
  end
110
119
 
111
120
  # app/shared/functions/kevin.py => /tmp/jets/demo/app_root/handlers/shared/functions/kevin.py
112
121
  def copy_source_as_handler(fun)
122
+ return if fun.internal?
123
+
113
124
  source_path = fun.source_file
114
125
  unless source_path
115
126
  attributes = fun.template.values.first
@@ -140,8 +151,11 @@ class Jets::Builders
140
151
  end
141
152
 
142
153
  def shared_ruby_shim(fun)
143
- vars = Jets::Builders::ShimVars::Shared.new(fun)
144
- generate_handler(vars)
154
+ # Cant use native_function because that requires task. Just re-implement
155
+ dest_path = fun.handler_dest
156
+ source_path = dest_path.sub(/^handlers/,'app')
157
+ FileUtils.mkdir_p(File.dirname(dest_path))
158
+ FileUtils.cp(source_path, dest_path)
145
159
  end
146
160
 
147
161
  def common_base_shim
@@ -27,29 +27,17 @@ class Jets::Cfn::Builders
27
27
  add_parameter(k, Description: k)
28
28
  end
29
29
 
30
- depends_on_params.each do |logical_id, desc|
31
- add_parameter(logical_id, Description: desc)
30
+ depends_on_params.each do |output_key, output_value|
31
+ desc = output_value.gsub("!GetAtt ", "") # desc doesnt allow !GetAtt
32
+ add_parameter(output_key, Description: desc)
32
33
  end
33
34
  end
34
35
 
35
36
  def depends_on_params
36
37
  return {} unless @app_class.depends_on
37
38
 
38
- params = {}
39
- @app_class.depends_on.each do |shared_stack|
40
- dependency = shared_stack.to_s.camelize # logical_id
41
- dependency_outputs(dependency).each do |output|
42
- dependency_class = dependency.to_s.classify
43
- desc = "From #{dependency_class}.Outputs.#{output}"
44
- # key: logical_id , value: description
45
- params[output] = desc
46
- end
47
- end
48
- params
49
- end
50
-
51
- def dependency_outputs(dependency)
52
- dependency.to_s.classify.constantize.output_keys
39
+ depends = Jets::Stack::Depends.new(@app_class.depends_on)
40
+ depends.params
53
41
  end
54
42
 
55
43
  def add_functions