ruby_yacht 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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