ruby_yacht 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.
Files changed (67) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile.lock +1 -1
  3. data/doc/TODO.md +16 -6
  4. data/doc/configuration_sample.rb +8 -9
  5. data/lib/ruby_yacht/dsl/app.rb +29 -1
  6. data/lib/ruby_yacht/dsl/app_type.rb +112 -0
  7. data/lib/ruby_yacht/dsl/configuration.rb +163 -2
  8. data/lib/ruby_yacht/dsl/dsl.rb +51 -9
  9. data/lib/ruby_yacht/dsl/hook.rb +105 -0
  10. data/lib/ruby_yacht/dsl/project.rb +17 -21
  11. data/lib/ruby_yacht/dsl.rb +2 -0
  12. data/lib/ruby_yacht/images/app/Dockerfile.erb +20 -13
  13. data/lib/ruby_yacht/images/app/before_startup.bash.erb +8 -0
  14. data/lib/ruby_yacht/images/app/checkout.bash +6 -0
  15. data/lib/ruby_yacht/images/app/startup.bash.erb +5 -0
  16. data/lib/ruby_yacht/images/app-dependencies/Dockerfile.erb +23 -5
  17. data/lib/ruby_yacht/images/database/Dockerfile.erb +25 -11
  18. data/lib/ruby_yacht/images/database/checkout.bash +10 -0
  19. data/lib/ruby_yacht/images/database/setup.bash +15 -0
  20. data/lib/ruby_yacht/images/web/Dockerfile.erb +4 -3
  21. data/lib/ruby_yacht/plugins/rails/scripts/install_gems.rb +3 -0
  22. data/lib/ruby_yacht/plugins/rails/scripts/load_seeds.rb +34 -0
  23. data/lib/ruby_yacht/{images/app/startup.rb → plugins/rails/scripts/prepare_rails_for_launch.rb} +2 -8
  24. data/lib/ruby_yacht/{images/app/update_database_config.rb → plugins/rails/scripts/update_rails_config.rb} +0 -1
  25. data/lib/ruby_yacht/plugins/rails.rb +40 -0
  26. data/lib/ruby_yacht/plugins.rb +6 -0
  27. data/lib/ruby_yacht/runner/build_images.rb +72 -5
  28. data/lib/ruby_yacht/runner/checkout.rb +1 -1
  29. data/lib/ruby_yacht.rb +2 -1
  30. data/ruby_yacht.gemspec +2 -1
  31. data/spec/docker/Dockerfile +1 -1
  32. data/spec/dsl/app_spec.rb +34 -0
  33. data/spec/dsl/app_type_spec.rb +176 -0
  34. data/spec/dsl/configuration_spec.rb +161 -2
  35. data/spec/dsl/dsl_spec.rb +3 -3
  36. data/spec/dsl/hook_spec.rb +111 -0
  37. data/spec/dsl/project_spec.rb +19 -83
  38. data/spec/fixtures/app-dependencies-dockerfile-generic +21 -0
  39. data/spec/fixtures/app-dependencies-dockerfile-generic-with-library-install +28 -0
  40. data/spec/fixtures/app-dependencies-dockerfile-rails +32 -0
  41. data/spec/fixtures/database-dockerfile +16 -15
  42. data/spec/fixtures/database-dockerfile-rails +35 -0
  43. data/spec/fixtures/database-dockerfile-with-seed-hooks +34 -0
  44. data/spec/fixtures/hooks/hook1.rb +0 -0
  45. data/spec/fixtures/hooks/hook2.rb +0 -0
  46. data/spec/fixtures/mars-before-startup +4 -0
  47. data/spec/fixtures/mars-before-startup-rails +7 -0
  48. data/spec/fixtures/mars-before-startup-with-before-startup-hooks +7 -0
  49. data/spec/fixtures/mars-dockerfile +9 -14
  50. data/spec/fixtures/mars-dockerfile-rails +33 -0
  51. data/spec/fixtures/mars-dockerfile-with-after-checkout-hooks +30 -0
  52. data/spec/fixtures/mars-dockerfile-with-before-startup-hooks +29 -0
  53. data/spec/fixtures/mars-startup +3 -0
  54. data/spec/fixtures/mars-startup-rails +3 -0
  55. data/spec/fixtures/multi-project-web-dockerfile +0 -10
  56. data/spec/fixtures/web-dockerfile +0 -6
  57. data/spec/plugins/rails_spec.rb +198 -0
  58. data/spec/runner/build_images_spec.rb +181 -15
  59. data/spec/runner/checkout_spec.rb +2 -2
  60. data/spec/support/test_project.rb +16 -8
  61. metadata +62 -30
  62. data/lib/ruby_yacht/images/app/checkout.rb +0 -7
  63. data/lib/ruby_yacht/images/app-dependencies/install_gems.rb +0 -12
  64. data/lib/ruby_yacht/images/database/load_seeds.rb +0 -42
  65. data/lib/ruby_yacht/images/database/setup.rb +0 -19
  66. data/lib/ruby_yacht/images/database/setup_database.sql.erb +0 -12
  67. data/spec/fixtures/app-dependencies-dockerfile +0 -25
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8fe86d0666f935e2212841266623b3f2fc4b0fdc
4
+ data.tar.gz: 5f5c2ed02c41aaf4f9b98dd3fc79e8110a43d2cc
5
+ SHA512:
6
+ metadata.gz: f4ad7a074ec77f4e8cfd1716ea69de349d0180125da4f7964420a1890c157c444a61eb7b06682e5dd672008fd55fdf01c4d037744286af4dd423be57cdccaa12
7
+ data.tar.gz: efc077fdd18c3485ce9f6ff25ad066c55dea2315324087aaa589a1ee6eb003b6bf0feb82c252ff9d4f96a91d335a0eb4a22d9a68cb341605edd3cc3d507d36f0
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ruby_yacht (0.1.0)
4
+ ruby_yacht (0.1.1)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/doc/TODO.md CHANGED
@@ -1,10 +1,20 @@
1
+ See the [GitHub Issue Tracker](https://github.com/brownleej/ruby-yacht/issues)
2
+ for open tickets.
3
+
1
4
  # General
2
5
 
3
- * Testing behavior of the scripts that run on the image.
4
6
  * Do more testing with remote databases and non-development environments
5
7
  * Consider whether deploy containers are worth including
6
8
  * Consider whether using the same web server for multiple projects is the best
7
9
  approach
10
+ * Look into issues with running shell command that has its own command-line
11
+ flags.
12
+
13
+ # Plugins
14
+
15
+ * Move more of the hooks to using the include_event helper
16
+ * Add documentation on plugin API
17
+ * Supporting shared databases between apps of different types
8
18
 
9
19
  # More Command Line Options
10
20
 
@@ -16,9 +26,9 @@
16
26
 
17
27
  * Add customizations for installation
18
28
  * Add customization for post-checkout hooks
19
- * HTTPS access for apps
20
- * Distinguishing whether we should use local or remote connections for other
21
- services
22
- * Different types of app servers, i.e. non-rails apps
23
29
  * Using one of the apps for the bare domain
24
- * Defining custom commands
30
+ * Defining custom commands
31
+ * Allowing customization points at different levels, like adding hooks to
32
+ individual apps
33
+ * Customizing how we check out the repositories, so that people can use https
34
+ instead of SSH.
@@ -23,6 +23,9 @@ RubyYacht.configure do
23
23
  # ~/.ssh/id_rsa into the containers.
24
24
  repository "github.com"
25
25
 
26
+ # The secret key that we use for session information in the Rails apps.
27
+ rails_secret_key_base 'abc'
28
+
26
29
  # The configuration for the database the app uses.
27
30
  database do
28
31
  # A database host of localhost tells ruby-yacht to build a database
@@ -37,18 +40,15 @@ RubyYacht.configure do
37
40
  password "test"
38
41
  end
39
42
 
40
- app :mars do
43
+ rails_app :mars do
41
44
  # This app's repository is at github.com/brownleej/mars
42
45
  repository_name 'brownleej/mars'
43
46
  end
44
47
 
45
- app :saturn do
48
+ rails_app :saturn do
46
49
  # This app's repository is at github.com/brownleej/saturn
47
50
  repository_name 'brownleej/saturn'
48
51
  end
49
-
50
- # The secret key that we use for session information in the Rails apps.
51
- secret_key_base 'abc'
52
52
  end
53
53
 
54
54
  # This is a second project that runs a different set of apps, with different
@@ -57,6 +57,7 @@ RubyYacht.configure do
57
57
  system_prefix :jupiter
58
58
  domain "jupiter.docker.local"
59
59
  repository "github.com"
60
+ rails_secret_key_base 'abc'
60
61
 
61
62
  database do
62
63
  # Because this host has a real domain, we will not build a database
@@ -71,15 +72,13 @@ RubyYacht.configure do
71
72
  password "test"
72
73
  end
73
74
 
74
- app :venus do
75
+ rails_app :venus do
75
76
  repository_name 'brownleej/venus'
76
77
  end
77
78
 
78
- app :saturn do
79
+ rails_app :saturn do
79
80
  repository_name 'brownleej/saturn'
80
81
  end
81
-
82
- secret_key_base 'abc'
83
82
  end
84
83
  end
85
84
 
@@ -10,6 +10,12 @@ module RubyYacht
10
10
  # To the project's repository domain.
11
11
  attr_accessor :repository_name
12
12
 
13
+ # The type of app this is, which determines what hooks and scripts we run
14
+ # when building it.
15
+ #
16
+ # For instance, this would be `rails` for a Rails app.
17
+ attr_accessor :app_type
18
+
13
19
  # The port that the app listens on.
14
20
  attr_accessor :port
15
21
 
@@ -37,8 +43,10 @@ module RubyYacht
37
43
  # ### Parameters
38
44
  #
39
45
  # * *name: String* The name of the app.
40
- def initialize(name)
46
+ def initialize(name, app_type = nil)
41
47
  @name = name.to_sym
48
+ @app_type = app_type
49
+ load_custom_attributes
42
50
  end
43
51
 
44
52
  add_attribute :name
@@ -48,12 +56,32 @@ module RubyYacht
48
56
  # You can call `repository_name 'foo/bar'` to set the app's `repository_name`.
49
57
  add_attribute :repository_name
50
58
 
59
+ ##
60
+ # :method: app_type
61
+ # You can call `app_type :foo` to set the app's `app_type`.
62
+ add_attribute :app_type
63
+
51
64
  ##
52
65
  # :method: port
53
66
  # You can call `port 3000` to set the app's `port`. It defaults to 8080.
54
67
  add_attribute :port, 8080
55
68
 
56
69
  creates_object App
70
+
71
+
72
+
73
+ # This method checks that all of the required attributes have been set on
74
+ # the object.
75
+ #
76
+ # If they haven't, this will raise an exception.
77
+ #
78
+ # It also checks that the app type has been defined in the configuration.
79
+ def check_required_attributes
80
+ super
81
+ unless RubyYacht.configuration.find_app_type(@app_type)
82
+ raise "App has invalid app type `#{@app_type}`"
83
+ end
84
+ end
57
85
  end
58
86
  end
59
87
  end
@@ -0,0 +1,112 @@
1
+ module RubyYacht
2
+ # This class represents a type of app that the user can configure.
3
+ #
4
+ # An app type corresponds to a major app framework, like Ruby on Rails. App
5
+ # types are defined by plugins, which also provide the logic for installing
6
+ # and running the apps.
7
+ #
8
+ # You can configure this with RubyYacht::AppType::DSL
9
+ class AppType
10
+ # The name of the type.
11
+ attr_accessor :name
12
+
13
+ # The attributes that we define on the project DSL once this app type has
14
+ # been loaded.
15
+ #
16
+ # Each entry will be a hash with a key for `name`, and optionally a key for
17
+ # `default` and `required`. These values will be given to the
18
+ # `add_attribute` method in the project DSL.
19
+ #
20
+ # The attribute names will be prefixed with the app type's name. For
21
+ # instance, if the `rails` app type provides an `environment` attribute,
22
+ # it will be called `rails_environment` on the project's DSL. This prevents
23
+ # conflicts with attributes from other plugins.
24
+ attr_accessor :project_attributes
25
+
26
+ # The attributes that we define on the app DSL once this app type has
27
+ # been loaded.
28
+ #
29
+ # Each entry will be a hash with a key for `name`, and optionally a key for
30
+ # `default` and `required`. These values will be given to the
31
+ # `add_attribute` method in the project DSL.
32
+ #
33
+ # The attributes will only be defined on apps with this app type.
34
+ #
35
+ # The attribute names will be prefixed with the app type's name. For
36
+ # instance, if the `rails` app type provides an `environment` attribute,
37
+ # it will be called `rails_environment` on the app's DSL. This prevents
38
+ # conflicts with attributes from other plugins.
39
+ attr_accessor :app_attributes
40
+
41
+ # The docker image that we use as the source for the app images.
42
+ attr_accessor :baseline_image
43
+
44
+ # The custom environment variables that we set in the images for this app
45
+ # type.
46
+ #
47
+ # Each entry will be a hash with the following keys:
48
+ #
49
+ # * **name: String** The name of the environment variable
50
+ # * **image: String** The type of image that this is set in (e.g. app
51
+ # or database).
52
+ # * **block** A block that will be called inside the Dockerfile
53
+ # ERB for providing the value of the environment
54
+ # variable.
55
+ attr_accessor :environment_variables
56
+
57
+ # This class provides a DSL for configuring a RubyYacht::AppType.
58
+ class DSL
59
+ include RubyYacht::DSL::Base
60
+ extend RubyYacht::DSL::Base::ClassMethods
61
+
62
+ # This initiailzer creates the app type DSL.
63
+ #
64
+ # ### Parameters
65
+ #
66
+ # * **name: Symbol** The name of the app type.
67
+ def initialize(name)
68
+ @name = name
69
+ @environment_variables = []
70
+ load_custom_attributes
71
+ end
72
+
73
+ add_attribute :name
74
+
75
+ #
76
+ # :method: baseline_image
77
+ # You can call `baseline_image 'ubuntu'` to use ubuntu as the source
78
+ # image for the app containers for this app type.
79
+ add_attribute :baseline_image
80
+
81
+ #
82
+ # :method: project_attribute
83
+ # You can call `project_attribute name: :environment, default: 'staging'`
84
+ # to add an attribute to the project DSL.
85
+ add_list :project_attribute
86
+
87
+ #
88
+ # :method: app_attribute
89
+ # You can call `app_attribute name: :environment, default: 'staging'`
90
+ # to add an attribute to the app DSL.
91
+ add_list :app_attribute
92
+
93
+ creates_object RubyYacht::AppType, [:name, :baseline_image,
94
+ :project_attributes, :app_attributes, :environment_variables]
95
+
96
+ # This method defines a new environment variable set for this app type.
97
+ #
98
+ # ### Parameters
99
+ #
100
+ # * **image: Symbol** The type of image this environment variable is set
101
+ # for.
102
+ # * **name: String** The name of the environment variable.
103
+ # * **block** A block for generating the environment variable.
104
+ # This will have access to the project (as @project)
105
+ # and the app (as @app) that we are building the
106
+ # image for.
107
+ def environment_variable(image, name, &block)
108
+ @environment_variables << {image: image, name: name, block: block}
109
+ end
110
+ end
111
+ end
112
+ end
@@ -1,4 +1,6 @@
1
1
  require_relative 'project'
2
+ require_relative 'hook'
3
+ require_relative 'app_type'
2
4
 
3
5
  module RubyYacht
4
6
  # This class stores the configuration for the system.
@@ -13,16 +15,65 @@ module RubyYacht
13
15
  # This method erases all the configuration.
14
16
  def clear
15
17
  @projects = []
18
+ @hooks = []
19
+ @app_types = []
16
20
  end
17
21
 
18
22
  # The projects that are part of this system.
19
23
  # Each entry is a RubyYacht::Project
20
24
  attr_accessor :projects
25
+
26
+ # The hooks to customize the build and run processes.
27
+ # Each entry is a RubyYacht::Hook
28
+ attr_accessor :hooks
29
+
30
+ # The app types that we can support.
31
+ # Each entry is a Symbol.
32
+ attr_accessor :app_types
33
+
34
+ # This method pulls up the hooks that we have defined.
35
+ #
36
+ # The hooks can be filtered by the attributes on the hook, like `event_type`
37
+ # and `event_time`.
38
+ #
39
+ # ### Parameters
40
+ #
41
+ # * **attributes: Hash** The attributes to look for in the returned
42
+ # hooks.
43
+ #
44
+ # ### Returns
45
+ #
46
+ # The matching hooks. This will be an Array where each item is a Hook.
47
+ def fetch_hooks(attributes={})
48
+ self.hooks.select do |hook|
49
+ attributes.keys.all? do |key|
50
+ hook.send(key) == attributes[key]
51
+ end
52
+ end
53
+ end
54
+
55
+ # This method finds an app type by name.
56
+ #
57
+ # ### Parameters
58
+ #
59
+ # * **name: Symbol** The name of the app type to return.
60
+ #
61
+ # ### Returns
62
+ #
63
+ # The RubyYacht::AppType with that name.
64
+ def find_app_type(name)
65
+ self.app_types.find { |type| type.name == name }
66
+ end
21
67
 
22
68
  # This method provides a DSL for top-level configuration.
23
69
  class DSL
24
70
  include RubyYacht::DSL::Base
25
71
  extend RubyYacht::DSL::Base::ClassMethods
72
+
73
+ # This initializer creates an empty configuration DSL.
74
+ def initialize
75
+ self.load_custom_attributes
76
+ end
26
77
 
27
78
  ##
28
79
  # :method: project
@@ -33,8 +84,109 @@ module RubyYacht
33
84
  # which you can use to configure the project, using
34
85
  # RubyYacht::Project::DSL.
35
86
  add_object_list :project, RubyYacht::Project::DSL
87
+
88
+ ##
89
+ # :method: hook
90
+ #
91
+ # This method adds a hook to the configuration.
92
+ #
93
+ # This takes hook's event_time and event_type as its arguments. It also
94
+ # takes a block which you can use to configure the hook, using
95
+ # RubyYacht::Hook::DSL.
96
+ add_object_list :hook, RubyYacht::Hook::DSL
97
+
98
+ ##
99
+ # :method: app_type
100
+ #
101
+ # This method adds an app type to the configuration.
102
+ #
103
+ # This takes type's name as its argument. It also takes a block which you
104
+ # can use to configure the type, using
105
+ # RubyYacht::AppType::DSL.
106
+ add_object_list :app_type, RubyYacht::AppType::DSL
107
+
108
+ # The path for files and other resources from configuration hooks.
109
+ attr_accessor :hook_folder
110
+
111
+ # The app type for hooks that we are defining in the current block.
112
+ attr_accessor :hook_app_type
113
+
114
+ # This method sets default attributes for a group of hooks.
115
+ #
116
+ # Any hooks that you create in the associated block will have their script
117
+ # folder and app type specified by the attached properties.
118
+ #
119
+ # ### Parameters
120
+ #
121
+ # * **folder: String** The full path to the script folder.
122
+ # * **block** A block for adding the hooks.
123
+ def add_hooks(options = {}, &block)
124
+ keys = [:folder, :app_type]
125
+ old_options = {}
126
+ keys.each do |key|
127
+ old_options[key] = self.send("hook_#{key}")
128
+ self.send("hook_#{key}=", options[key]) if options.has_key?(key)
129
+ end
130
+
131
+ block.call
132
+
133
+ keys.each do |key|
134
+ self.send("hook_#{key}=", old_options[key])
135
+ end
136
+ end
137
+
138
+ # This method adds a before hook.
139
+ #
140
+ # ### Parameters
141
+ #
142
+ # * **event_type: Symbol** The event type for the new hook.
143
+ # * **block** A block for configuring the hook. You can
144
+ # call the RubyYacht::Hook::DSL methods in
145
+ # this block.
146
+ def before(event_type, &block)
147
+ add_hook :before, event_type, &block
148
+ end
149
+
150
+ # This method adds an after hook.
151
+ #
152
+ # ### Parameters
153
+ #
154
+ # * **event_type: Symbol** The event type for the new hook.
155
+ # * **block** A block for configuring the hook. You can
156
+ # call the RubyYacht::Hook::DSL methods in
157
+ # this block.
158
+ def after(event_type, &block)
159
+ add_hook :after, event_type, &block
160
+ end
161
+
162
+ # This method adds a during hook.
163
+ #
164
+ # This hook will be run while event is happening, and provides the core
165
+ # logic for the event.
166
+ #
167
+ # ### Parameters
168
+ #
169
+ # * **event_type: Symbol** The event type for the new hook.
170
+ # * **block** A block for configuring the hook. You can
171
+ # call the RubyYacht::Hook::DSL methods in
172
+ # this block.
173
+ def during(event_type, &block)
174
+ add_hook :during, event_type, &block
175
+ end
36
176
 
37
- creates_object Configuration
177
+ creates_object Configuration, %w(projects hooks app_types)
178
+
179
+ private
180
+
181
+ def add_hook(event_time, event_type, &block)
182
+ folder = hook_folder
183
+ type = hook_app_type
184
+ hook event_time, event_type do
185
+ script_folder folder
186
+ app_type type
187
+ instance_eval(&block)
188
+ end
189
+ end
38
190
  end
39
191
  end
40
192
 
@@ -47,7 +199,16 @@ module RubyYacht
47
199
  # of projects.
48
200
  def self.configure(&block)
49
201
  new_configuration = Configuration::DSL.new.run(block).create_object
50
- self.configuration.projects += new_configuration.projects
202
+ %w(projects hooks).each do |field|
203
+ self.configuration.send("#{field}=", self.configuration.send(field) + new_configuration.send(field))
204
+ end
205
+
206
+ new_configuration.app_types.each do |type|
207
+ if self.configuration.app_types.any? { |existing| existing.name == type.name }
208
+ raise "App type already registered: #{type.name}"
209
+ end
210
+ self.configuration.app_types << type
211
+ end
51
212
  end
52
213
 
53
214
  # This method gets the current configuration for the system.
@@ -130,8 +130,8 @@ module RubyYacht
130
130
  #
131
131
  # * **name: Symbol** The name of the method, which will also be the
132
132
  # name of the attribute.
133
- # * **type: String** The name of the type that provides the DSL when
134
- # they call this method.
133
+ # * **type: Class** The class type that provides the DSL when they
134
+ # call this method.
135
135
  # * **options: Hash** Other options about the DSL. Right now the only
136
136
  # option is *required*, which indicates whether
137
137
  # they must define this configuraiton.
@@ -157,14 +157,17 @@ module RubyYacht
157
157
  #
158
158
  # * **name: Symbol** The name of the method. The attribute name will
159
159
  # be this, followed by an "s"
160
- # * **type: String** The name of the type that provides the type when
161
- # they call this method.
160
+ # * **type: Class** The class type that provides the DSL when they
161
+ # call this method.
162
162
  def add_object_list(name, type)
163
163
  add_generic_attribute name, "#{name}s", [], false do |*args, &config_block|
164
164
  variable_name = "@#{name}s"
165
165
  list = instance_variable_get(variable_name)
166
166
  object_config = type.new(*args)
167
- value = object_config.run(config_block).create_object
167
+ if config_block
168
+ object_config.run(config_block)
169
+ end
170
+ value = object_config.create_object
168
171
  list << value
169
172
  instance_variable_set(variable_name, list)
170
173
  end
@@ -186,6 +189,46 @@ module RubyYacht
186
189
  @created_type = type
187
190
  end
188
191
  end
192
+
193
+ # This initializer creates a new DSL instance.
194
+ def initialize
195
+ load_custom_attributes
196
+ end
197
+
198
+ # This method adds attributes to this DSL instance's singleton DSL, based
199
+ # on the attributes defined by the app types that have been loaded.
200
+ def load_custom_attributes
201
+ full_class = self.class
202
+ self.singleton_class.instance_eval do
203
+ @copied_attributes = (full_class.copied_attributes || []).dup
204
+ @all_attributes = full_class.all_attributes.dup
205
+ @required_attributes = full_class.required_attributes.dup
206
+ @default_values = full_class.default_values.dup
207
+ end
208
+
209
+ if self.class.created_type
210
+ dsl_type = self.class.created_type.name.split('::').last.downcase
211
+ copied_attributes = self.class.copied_attributes
212
+
213
+ RubyYacht.configuration.app_types.each do |app_type|
214
+ next if @app_type && app_type.name != @app_type
215
+ attributes = app_type.send("#{dsl_type}_attributes") || [] rescue []
216
+ attributes.each do |attribute|
217
+ if attribute.has_key?(:required)
218
+ required = attribute[:required]
219
+ else
220
+ required = true
221
+ end
222
+ name = "#{app_type.name}_#{attribute[:name]}"
223
+ self.singleton_class.add_attribute name, attribute[:default], required
224
+ self.singleton_class.copied_attributes << name
225
+ self.class.created_type.instance_eval do
226
+ attr_accessor name
227
+ end
228
+ end
229
+ end
230
+ end
231
+ end
189
232
 
190
233
  # This method runs a block with DSL methods.
191
234
  #
@@ -199,7 +242,7 @@ module RubyYacht
199
242
  #
200
243
  # * **block: Proc** The block to run.
201
244
  def run(block)
202
- defaults = self.class.default_values || {}
245
+ defaults = self.singleton_class.default_values || {}
203
246
  defaults.each do |name, value|
204
247
  copy =
205
248
  case value
@@ -219,7 +262,7 @@ module RubyYacht
219
262
  #
220
263
  # If they haven't, this will raise an exception.
221
264
  def check_required_attributes
222
- attributes = self.class.required_attributes || []
265
+ attributes = self.singleton_class.required_attributes || []
223
266
  attributes.each do |name|
224
267
  value = instance_variable_get("@#{name}")
225
268
  if value == nil || value == ''
@@ -241,8 +284,7 @@ module RubyYacht
241
284
  def create_object
242
285
  check_required_attributes
243
286
  object = self.class.created_type.new
244
-
245
- self.class.copied_attributes.each do |name|
287
+ self.singleton_class.copied_attributes.each do |name|
246
288
  object.send("#{name}=", instance_variable_get("@#{name}"))
247
289
  end
248
290
  object
@@ -0,0 +1,105 @@
1
+ module RubyYacht
2
+ # This class provides a hook that a user or plugin can provide to customize
3
+ # the app images.
4
+ #
5
+ # You can configure this with RubyYacht::Hook::DSL
6
+ class Hook
7
+ # The permitted values for the `event_time` attribute.
8
+ EVENT_TIMES = [:before, :after, :during]
9
+
10
+ # The permitted values for the `event_type` attribute.
11
+ EVENT_TYPES = [:startup, :build_checkout, :install_libraries]
12
+
13
+ # When this hook should take effect, relative to the event.
14
+ attr_accessor :event_time
15
+
16
+ # The type of event that this hook is attached to.
17
+ attr_accessor :event_type
18
+
19
+ # The type of app that this hook applies to.
20
+ attr_accessor :app_type
21
+
22
+ # The path to the script that contains the code for this hook.
23
+ attr_accessor :script_path
24
+
25
+ # The command that we should run for this hook.
26
+ attr_accessor :command
27
+
28
+ # The name of the file containing the code for this hook.
29
+ def script_name
30
+ return '' unless script_path
31
+ File.basename(script_path)
32
+ end
33
+
34
+ # This class provides a DSL for configuring hooks.
35
+ class DSL
36
+ include RubyYacht::DSL::Base
37
+ extend RubyYacht::DSL::Base::ClassMethods
38
+
39
+ # This initializer creates a new Hook DSL.
40
+ #
41
+ # ### Parameters
42
+ #
43
+ # * **event_time: Symbol** The time for the hook, relative to the
44
+ # event.
45
+ # * **event_type: Symbol** The type of event the hook is attached to.
46
+ def initialize(event_time, event_type)
47
+ @event_time = event_time
48
+ @event_type = event_type
49
+ @script_path = nil
50
+ load_custom_attributes
51
+ end
52
+
53
+ ##
54
+ # :method: app_type
55
+ #
56
+ # You can call `app_type :rails` to signify that this hook applies to
57
+ # rails apps.
58
+ add_attribute :app_type
59
+
60
+ ##
61
+ # :method: command
62
+ #
63
+ # You can call `command 'whoami'` to signify that this hook runs the
64
+ # command `whoami`. You can also set the command through `run_script`, if
65
+ # the hook runs a script file included with the plugin.
66
+ add_attribute :command
67
+
68
+ ##
69
+ # :method: script_folder
70
+ #
71
+ # You can call `script_folder './scripts'` to signify that the scripts for
72
+ # this hook are found in the `./scripts` folder.
73
+ add_attribute :script_folder, nil, false
74
+
75
+ # This method sets the name of the script containing the code for the
76
+ # hook.
77
+ #
78
+ # The script will be understood to be in the script folder that was given
79
+ # when the DSL was created.
80
+ #
81
+ # ### Parameters
82
+ #
83
+ # * **name: String** The filename of the script.
84
+ def run_script(name)
85
+ @script_path = File.join(@script_folder || '.', name)
86
+ @command = "/var/docker/#{name}"
87
+ end
88
+
89
+ # This method checks that all of the required attributes have been set on
90
+ # the object.
91
+ #
92
+ # If they haven't, this will raise an exception.
93
+ #
94
+ # This will also check that the app type is valid.
95
+ def check_required_attributes
96
+ super
97
+ unless RubyYacht.configuration.find_app_type(@app_type)
98
+ raise "Hook has invalid app type `#{@app_type}`"
99
+ end
100
+ end
101
+
102
+ creates_object RubyYacht::Hook, %w(event_time event_type script_path app_type command)
103
+ end
104
+ end
105
+ end