ruby_yacht 0.6.1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/README.md +7 -5
  4. data/doc/TODO.md +1 -5
  5. data/doc/configuration.md +51 -0
  6. data/doc/plugins.md +4 -0
  7. data/lib/ruby_yacht/dsl/configuration.rb +26 -1
  8. data/lib/ruby_yacht/dsl/dsl.rb +35 -3
  9. data/lib/ruby_yacht/dsl/hook.rb +4 -0
  10. data/lib/ruby_yacht/dsl/project.rb +11 -0
  11. data/lib/ruby_yacht/images/app/Dockerfile.erb +2 -1
  12. data/lib/ruby_yacht/images/app/checkout.bash +1 -1
  13. data/lib/ruby_yacht/images/app-dependencies/Dockerfile.erb +7 -1
  14. data/lib/ruby_yacht/images/database/Dockerfile.erb +3 -1
  15. data/lib/ruby_yacht/images/database/checkout.bash +3 -4
  16. data/lib/ruby_yacht/images/web/Dockerfile.erb +2 -0
  17. data/lib/ruby_yacht/plugins/nginx.rb +5 -0
  18. data/lib/ruby_yacht/plugins/rails/scripts/load_seeds.rb +10 -1
  19. data/lib/ruby_yacht/plugins/rails/scripts/prepare_rails_for_launch.rb +1 -1
  20. data/lib/ruby_yacht/plugins/rails/scripts/update_rails_config.rb +6 -4
  21. data/lib/ruby_yacht/plugins/rails.rb +30 -17
  22. data/lib/ruby_yacht/plugins.rb +1 -1
  23. data/lib/ruby_yacht/runner/build_images.rb +5 -7
  24. data/lib/ruby_yacht/runner/run_containers.rb +1 -2
  25. data/ruby_yacht.gemspec +2 -2
  26. data/spec/docker/run.bash +4 -0
  27. data/spec/dsl/configuration_spec.rb +29 -0
  28. data/spec/dsl/dsl_spec.rb +56 -0
  29. data/spec/dsl/hook_spec.rb +28 -5
  30. data/spec/dsl/project_spec.rb +46 -0
  31. data/spec/dsl/server_type_spec.rb +3 -0
  32. data/spec/fixtures/app-dependencies-dockerfile-generic +3 -1
  33. data/spec/fixtures/app-dependencies-dockerfile-generic-with-library-install +3 -1
  34. data/spec/fixtures/app-dependencies-dockerfile-rails +4 -3
  35. data/spec/fixtures/app-dependencies-dockerfile-rails-production +35 -0
  36. data/spec/fixtures/app-dependencies-dockerfile-with-https-repository +25 -0
  37. data/spec/fixtures/app-dependencies-dockerfile-with-no-repository +2 -0
  38. data/spec/fixtures/database-dockerfile +1 -1
  39. data/spec/fixtures/database-dockerfile-mysql +1 -1
  40. data/spec/fixtures/database-dockerfile-rails +2 -6
  41. data/spec/fixtures/database-dockerfile-rails-mysql +38 -0
  42. data/spec/fixtures/database-dockerfile-rails-with-no-repository +1 -3
  43. data/spec/fixtures/database-dockerfile-with-seed-hooks +1 -1
  44. data/spec/fixtures/local_config.yml +8 -0
  45. data/spec/fixtures/mars-dockerfile +0 -1
  46. data/spec/fixtures/mars-dockerfile-rails +0 -1
  47. data/spec/fixtures/mars-dockerfile-rails-with-no-repository +0 -1
  48. data/spec/fixtures/mars-dockerfile-with-after-checkout-hooks +0 -1
  49. data/spec/fixtures/mars-dockerfile-with-before-startup-hooks +0 -1
  50. data/spec/fixtures/mars-dockerfile-with-custom-file-copy +0 -1
  51. data/spec/fixtures/mars-dockerfile-with-local-database +0 -1
  52. data/spec/fixtures/mars-dockerfile-with-no-repository +0 -1
  53. data/spec/fixtures/mars-dockerfile-with-remote-database +0 -1
  54. data/spec/fixtures/mars-startup-rails +1 -1
  55. data/spec/fixtures/mars-startup-rails-with-no-repository +1 -1
  56. data/spec/fixtures/web-dockerfile-nginx +3 -0
  57. data/spec/integration/01_normal_config_spec.rb +352 -0
  58. data/spec/integration/{create_new_project_spec.rb → 02_creating_new_project_spec.rb} +2 -2
  59. data/spec/integration/03_no_repository_spec.rb +56 -0
  60. data/spec/integration/04_no_database_spec.rb +58 -0
  61. data/spec/integration/05_https_repository_spec.rb +52 -0
  62. data/spec/integration/06_production_environment_spec.rb +72 -0
  63. data/spec/integration/07_local_checkout_spec.rb +52 -0
  64. data/spec/integration/08_multiple_projects_spec.rb +46 -0
  65. data/spec/integration/09_titular_app_spec.rb +53 -0
  66. data/spec/integration/10_misc_scenarios_spec.rb +97 -0
  67. data/spec/integration/run.rb +18 -2
  68. data/spec/plugins/rails_spec.rb +73 -16
  69. data/spec/runner/build_images_spec.rb +17 -1
  70. data/spec/runner/run_containers_spec.rb +3 -3
  71. data/spec/support/integration_helpers.rb +18 -4
  72. metadata +30 -20
  73. data/spec/integration/build_images_spec.rb +0 -210
  74. data/spec/integration/build_spec.rb +0 -23
  75. data/spec/integration/checkout_spec.rb +0 -94
  76. data/spec/integration/implode_spec.rb +0 -20
  77. data/spec/integration/run_containers_spec.rb +0 -279
  78. data/spec/integration/services_spec.rb +0 -99
  79. data/spec/integration/shell_spec.rb +0 -31
  80. data/spec/integration/update_hosts_spec.rb +0 -35
@@ -17,11 +17,13 @@ common_config = {
17
17
  'port' => ENV['DATABASE_PORT']
18
18
  }
19
19
 
20
+ rails_env = ENV['RAILS_ENV'] || 'development'
21
+
20
22
  database_config = {
21
- ENV['RAILS_ENV'] => common_config
23
+ rails_env => common_config
22
24
  }
23
25
 
24
- if ENV['RAILS_ENV'] == 'development'
26
+ if rails_env == 'development'
25
27
  database_config['test'] = common_config.dup
26
28
  database_config['test']['database'] += '_test'
27
29
  end
@@ -31,12 +33,12 @@ File.open('/var/code/config/database.yml', 'w') do |file|
31
33
  end
32
34
 
33
35
  secret_key_config = {
34
- ENV['RAILS_ENV'] => {
36
+ rails_env => {
35
37
  'secret_key_base' => ENV['SECRET_KEY_BASE']
36
38
  }
37
39
  }
38
40
 
39
- if ENV['RAILS_ENV'] == 'development'
41
+ if rails_env == 'development'
40
42
  secret_key_config['test'] = secret_key_config['development'].dup
41
43
  end
42
44
 
@@ -5,6 +5,7 @@ module RubyYacht::Plugins
5
5
  def self.load
6
6
  load_server_type
7
7
  load_hooks
8
+ load_environment_variables
8
9
  end
9
10
 
10
11
  # This method loads the configuration for the Rails server type.
@@ -24,26 +25,16 @@ module RubyYacht::Plugins
24
25
  def self.load_hooks
25
26
  RubyYacht.configure do
26
27
  add_hooks(app_server_type: :rails, container_type: :app, script_folder: File.join(File.dirname(__FILE__), 'rails', 'scripts')) do
27
- during :initialize_app_environment do
28
- set_environment_variable 'RAILS_ENV' do
29
- @project.rails_environment
30
- end
31
- set_environment_variable 'SECRET_KEY_BASE' do
32
- @project.rails_secret_key_base
33
- end
34
- set_environment_variable 'EXCLUDED_GEM_GROUPS' do
35
- groups = @project.rails_excluded_gem_groups.join(' ')
36
- groups = '""' if groups == ''
37
- groups
38
- end
39
- end
40
28
  during(:install_libraries) { run_script :ruby, 'install_gems.rb' }
41
29
 
42
30
  after(:build_checkout) { command 'bundle install --clean' }
43
31
 
44
- during :load_database_seeds do
45
- container_type :database
46
- run_script :ruby, 'load_seeds.rb'
32
+ if RubyYacht.configuration.find_server_type(:mysql)
33
+ during :load_database_seeds do
34
+ container_type :database
35
+ database_server_type :mysql
36
+ run_script :ruby, 'load_seeds.rb'
37
+ end
47
38
  end
48
39
 
49
40
  during :build_new_app do
@@ -53,7 +44,29 @@ module RubyYacht::Plugins
53
44
  before(:startup) { run_script :ruby, 'update_rails_config.rb' }
54
45
  before(:startup) { run_script :ruby, 'prepare_rails_for_launch.rb' }
55
46
 
56
- during(:startup) { command 'rails s -b 0.0.0.0 -p $APP_PORT -e $RAILS_ENV' }
47
+ during(:startup) { command 'rails s -b 0.0.0.0 -p $APP_PORT' }
48
+ end
49
+ end
50
+ end
51
+
52
+ # This method loads the hooks for setting environment variables for the
53
+ # Rails apps.
54
+ def self.load_environment_variables
55
+ RubyYacht.configure do
56
+ add_hooks app_server_type: :rails, container_type: :app do
57
+ during :initialize_app_environment do
58
+ set_environment_variable 'RAILS_ENV' do
59
+ @project.rails_environment == 'development' ? nil : @project.rails_environment
60
+ end
61
+ set_environment_variable 'SECRET_KEY_BASE' do
62
+ @project.rails_secret_key_base
63
+ end
64
+ set_environment_variable 'EXCLUDED_GEM_GROUPS' do
65
+ groups = @project.rails_excluded_gem_groups.join(' ')
66
+ groups = '""' if groups == ''
67
+ groups
68
+ end
69
+ end
57
70
  end
58
71
  end
59
72
  end
@@ -3,6 +3,6 @@ module RubyYacht
3
3
  module Plugins
4
4
  end
5
5
  end
6
- require 'ruby_yacht/plugins/rails'
7
6
  require 'ruby_yacht/plugins/mysql'
7
+ require 'ruby_yacht/plugins/rails'
8
8
  require 'ruby_yacht/plugins/nginx'
@@ -15,12 +15,10 @@ module RubyYacht::Runner
15
15
  def run
16
16
  FileUtils.mkdir_p 'tmp'
17
17
 
18
- default_system_prefix = projects.first.system_prefix
19
- docker "network create #{default_system_prefix} 2> /dev/null"
20
-
21
18
  projects.each do |project|
22
19
  log "Building images for #{project.name}"
23
20
 
21
+ docker "network create #{project.system_prefix} 2> /dev/null"
24
22
  id_path = File.join(ENV['HOME'], '.ssh', 'id_rsa')
25
23
  tmp_id_path = File.join('tmp', 'id_rsa')
26
24
  FileUtils.cp(id_path, tmp_id_path) if File.exist?(id_path)
@@ -133,10 +131,10 @@ module RubyYacht::Runner
133
131
 
134
132
  @hook_events =
135
133
  case folder_name
136
- when 'app' then [:startup, :build_checkout, :build_new_app]
137
- when 'app-dependencies' then [:initialize_app_environment, :install_libraries]
138
- when 'database' then [:create_databases, :install_libraries, :load_database_seeds]
139
- when 'web' then [:install_libraries, :add_project_landing, :add_app_config]
134
+ when 'app' then [:startup, :build_checkout, :build_new_app, :cleanup]
135
+ when 'app-dependencies' then [:initialize_app_environment, :install_libraries, :cleanup]
136
+ when 'database' then [:create_databases, :install_libraries, :load_database_seeds, :cleanup]
137
+ when 'web' then [:install_libraries, :add_project_landing, :add_app_config, :cleanup]
140
138
  else []
141
139
  end
142
140
 
@@ -11,10 +11,9 @@ module RubyYacht::Runner
11
11
 
12
12
  # This method runs the logic of the command.
13
13
  def run
14
- @network = projects.first.system_prefix
15
-
16
14
  projects.each do |project|
17
15
  @project = project
16
+ @network = project.system_prefix
18
17
 
19
18
  project.databases.select(&:local?).each do |database|
20
19
  run_container database
data/ruby_yacht.gemspec CHANGED
@@ -1,7 +1,7 @@
1
1
  Gem::Specification.new do |spec|
2
2
  spec.name = 'ruby_yacht'
3
- spec.version = '0.6.1'
4
- spec.date = '2016-05-16'
3
+ spec.version = '0.7.0'
4
+ spec.date = '2016-05-20'
5
5
  spec.description = "A DSL for building docker containers for a family of Rails apps"
6
6
  spec.summary = "A DSL for building docker containers for a family of Rails apps"
7
7
  spec.authors = ["John Brownlee"]
data/spec/docker/run.bash CHANGED
@@ -31,6 +31,10 @@ else
31
31
  EXISTING_CONTAINER=""
32
32
  fi
33
33
 
34
+ if [[ -n $VERBOSE_DOCKER && $VERBOSE_DOCKER -eq 1 ]]; then
35
+ EXTRA_FLAGS="$EXTRA_FLAGS -e VERBOSE_DOCKER=1"
36
+ fi
37
+
34
38
  if [ -z "$EXISTING_CONTAINER" ]; then
35
39
  docker run -it $EXTRA_FLAGS --privileged --name=ruby-yacht-tests -v $PWD:/var/code/ruby-yacht ruby-yacht-tests /var/docker/startup.bash $*
36
40
  RESULT=$?
@@ -339,6 +339,24 @@ describe RubyYacht::Configuration do
339
339
  expect(configuration.server_types[0].server_attributes.count).to eq 1
340
340
  end
341
341
 
342
+ it 'can add local config' do
343
+ configuration = RubyYacht::Configuration::DSL.new.run(Proc.new do
344
+ add_local_config File.join(File.dirname(__FILE__), '..', 'fixtures', 'local_config.yml')
345
+ end)
346
+
347
+ expect(configuration.local_config).to eq(
348
+ system_prefix: 'jupiter',
349
+ check_out_locally: true,
350
+ rails_environment: 'production',
351
+ database: {
352
+ host: 'localhost',
353
+ name: 'jupiter_testing',
354
+ username: 'jupiter',
355
+ password: 'jupiterpass'
356
+ }
357
+ )
358
+ end
359
+
342
360
  it "can add `during` hooks" do
343
361
  configuration = RubyYacht::Configuration::DSL.new.run(Proc.new do
344
362
  add_hooks container_type: :app, app_server_type: :configuration_test, script_folder: './scripts' do
@@ -408,6 +426,17 @@ describe RubyYacht::Configuration do
408
426
  expect(configuration.server_types[2].name).to eq :second_configuration_test
409
427
  end
410
428
 
429
+ it "adds to the local config" do
430
+ RubyYacht.configuration.local_config = {system_prefix: 'apollo', rails_secret_key_base: 'ABC1234'}
431
+ RubyYacht.configure do
432
+ add_local_config File.join(File.dirname(__FILE__), '..', 'fixtures', 'local_config.yml')
433
+ end
434
+
435
+ configuration = RubyYacht.configuration
436
+ expect(configuration.local_config[:system_prefix]).to eq 'jupiter'
437
+ expect(configuration.local_config[:rails_secret_key_base]).to eq 'ABC1234'
438
+ end
439
+
411
440
  context "with a server type that is already registered" do
412
441
  before do
413
442
  type = RubyYacht::ServerType.new
data/spec/dsl/dsl_spec.rb CHANGED
@@ -298,6 +298,62 @@ describe RubyYacht::DSL::Base do
298
298
  end
299
299
  end
300
300
 
301
+ describe 'copy_local_config' do
302
+ before do
303
+ DSLTestHelpers::DSL.add_attribute :name
304
+ DSLTestHelpers::DSL.add_attribute :height
305
+ DSLTestHelpers::DSL.add_attribute :gender
306
+ DSLTestHelpers::DSL.add_boolean :vegan
307
+
308
+ @dsl = DSLTestHelpers::DSL.new
309
+ RubyYacht.configuration.local_config = {name: 'John', height: 120, vegan: false, last_name: 'Smith'}
310
+ end
311
+
312
+ context 'with a valid attribute' do
313
+ it 'copies the value from the local config' do
314
+ @dsl.copy_local_config(:name)
315
+ expect(@dsl.instance_eval { @name }).to eq 'John'
316
+ end
317
+ end
318
+
319
+ context 'with a path to the dictionary' do
320
+ it 'copies the value from the local config' do
321
+ RubyYacht.configuration.local_config = {people: {john: {height: 150}}}
322
+ @dsl.copy_local_config :height, from: 'people.john'
323
+ expect(@dsl.instance_eval { @height }).to eq 150
324
+ end
325
+ end
326
+
327
+ context 'with a false boolean attribute' do
328
+ it 'leaves the value false' do
329
+ @dsl.copy_local_config :vegan
330
+ expect(@dsl.instance_eval { @vegan }).to be_falsey
331
+ end
332
+ end
333
+
334
+ context 'with a true boolean attribute' do
335
+ it 'sets the value to true' do
336
+ RubyYacht.configuration.local_config[:vegan] = true
337
+ @dsl.copy_local_config :vegan
338
+ expect(@dsl.instance_eval { @vegan }).to be_truthy
339
+ end
340
+ end
341
+
342
+ context 'with an attribute that is not defined on the DSL' do
343
+ it 'raises an exception' do
344
+ expect { @dsl.copy_local_config :last_name }.to raise_exception 'Undefined field in DSLTestHelpers::DSL: last_name'
345
+ end
346
+ end
347
+
348
+ context 'with an attribute with no value in the config file' do
349
+ it 'leaves the value nil' do
350
+ @dsl.copy_local_config :gender
351
+ expect(@dsl.instance_eval { @gender }).to be_nil
352
+ end
353
+ end
354
+ end
355
+
356
+
301
357
  describe "check_required_attributes" do
302
358
  before do
303
359
  DSLTestHelpers::DSL.add_attribute :name
@@ -335,14 +335,37 @@ describe RubyYacht::Hook do
335
335
  end
336
336
 
337
337
  describe "environment variable behavior" do
338
- let(:behavior) { RubyYacht::Hook::EnvironmentVariableBehavior.new('SECRET_KEY_BASE', Proc.new { 'abc'}) }
338
+ context 'with a normal value' do
339
+ let(:behavior) { RubyYacht::Hook::EnvironmentVariableBehavior.new('SECRET_KEY_BASE', Proc.new { 'abc'}) }
339
340
 
340
- it 'sets the environment variable as its docker command' do
341
- expect(behavior.dockerfile_command).to eq 'ENV SECRET_KEY_BASE abc'
341
+ it 'sets the environment variable as its docker command' do
342
+ expect(behavior.dockerfile_command).to eq 'ENV SECRET_KEY_BASE abc'
343
+ end
344
+
345
+ it 'sets the environment variable as its shell command' do
346
+ expect(behavior.shell_command).to eq 'export SECRET_KEY_BASE="abc"'
347
+ end
342
348
  end
343
349
 
344
- it 'sets the environment variable as its shell command' do
345
- expect(behavior.shell_command).to eq 'export SECRET_KEY_BASE="abc"'
350
+ context 'with a blank value' do
351
+ let(:behavior) { RubyYacht::Hook::EnvironmentVariableBehavior.new('SECRET_KEY_BASE', Proc.new { '' }) }
352
+
353
+ it 'sets the environment variable as its docker command' do
354
+ expect(behavior.dockerfile_command).to eq 'ENV SECRET_KEY_BASE '
355
+ end
356
+
357
+ it 'sets the environment variable as its shell command' do
358
+ expect(behavior.shell_command).to eq 'export SECRET_KEY_BASE=""'
359
+ end
360
+ end
361
+
362
+ context 'with a nil value' do
363
+ let(:behavior) { RubyYacht::Hook::EnvironmentVariableBehavior.new('SECRET_KEY_BASE', Proc.new { nil }) }
364
+
365
+ it 'has a nil command' do
366
+ expect(behavior.dockerfile_command).to be_nil
367
+ expect(behavior.shell_command).to be_nil
368
+ end
346
369
  end
347
370
  end
348
371
  end
@@ -26,6 +26,7 @@ describe RubyYacht::Project do
26
26
  @builder = Proc.new do
27
27
  system_prefix :a
28
28
  repository "github.com"
29
+ repository_protocol :https
29
30
 
30
31
  app :generic, :app1 do
31
32
  repository_name 'brownleej/test1'
@@ -57,6 +58,7 @@ describe RubyYacht::Project do
57
58
  expect(project.name).to eq :test_project
58
59
  expect(project.system_prefix).to eq :a
59
60
  expect(project.repository).to eq "github.com"
61
+ expect(project.repository_protocol).to eq :https
60
62
  expect(project.primary_app).to eq :app1
61
63
 
62
64
  expect(project.apps.map(&:name)).to eq [:app1, :app2]
@@ -68,6 +70,9 @@ describe RubyYacht::Project do
68
70
 
69
71
  it "requires the system prefix" do
70
72
  @builder = Proc.new do
73
+ repository 'github.com'
74
+ repository_protocol :https
75
+
71
76
  app :generic, :app1 do
72
77
  repository_name 'brownleej/test1'
73
78
  end
@@ -101,6 +106,7 @@ describe RubyYacht::Project do
101
106
  it "requires the repository" do
102
107
  @builder = Proc.new do
103
108
  system_prefix :a
109
+ repository_protocol :https
104
110
 
105
111
  app :generic, :app1 do
106
112
  repository_name 'brownleej/test1'
@@ -132,10 +138,46 @@ describe RubyYacht::Project do
132
138
  expect { project }.to raise_exception "Missing required attribute repository for RubyYacht::Project::DSL"
133
139
  end
134
140
 
141
+ it 'defaults the repository protocol to ssh' do
142
+ @builder = Proc.new do
143
+ system_prefix :a
144
+ repository 'github.com'
145
+
146
+ app :generic, :app1 do
147
+ repository_name 'brownleej/test1'
148
+ end
149
+
150
+ app :generic, :app2 do
151
+ repository_name 'brownleej/test2'
152
+ end
153
+
154
+ database :sqlite, :project1 do
155
+ host "localhost"
156
+ username "test"
157
+ password "test"
158
+ port 1234
159
+ container_label :sqlite
160
+ end
161
+
162
+ web_server :apache, :web do
163
+ domain 'test.com'
164
+ end
165
+
166
+ dns_server do
167
+ server '8.10.1.1'
168
+ end
169
+
170
+ primary_app :app1
171
+ end
172
+
173
+ expect(project.repository_protocol).to eq :ssh
174
+ end
175
+
135
176
  it "does not require any apps" do
136
177
  @builder = Proc.new do
137
178
  system_prefix :a
138
179
  repository "github.com"
180
+ repository_protocol :https
139
181
 
140
182
  database :sqlite, :project1 do
141
183
  host "localhost"
@@ -163,6 +205,7 @@ describe RubyYacht::Project do
163
205
  @builder = Proc.new do
164
206
  system_prefix :a
165
207
  repository "github.com"
208
+ repository_protocol :https
166
209
 
167
210
  app :generic, :app1 do
168
211
  repository_name 'brownleej/test1'
@@ -194,6 +237,7 @@ describe RubyYacht::Project do
194
237
  @builder = Proc.new do
195
238
  system_prefix :a
196
239
  repository "github.com"
240
+ repository_protocol :https
197
241
 
198
242
  app :generic, :app1 do
199
243
  repository_name 'brownleej/test1'
@@ -221,6 +265,7 @@ describe RubyYacht::Project do
221
265
  @builder = Proc.new do
222
266
  system_prefix :a
223
267
  repository "github.com"
268
+ repository_protocol :https
224
269
 
225
270
  app :generic, :app1 do
226
271
  repository_name 'brownleej/test1'
@@ -252,6 +297,7 @@ describe RubyYacht::Project do
252
297
  @builder = Proc.new do
253
298
  system_prefix :a
254
299
  repository "github.com"
300
+ repository_protocol :https
255
301
 
256
302
  app :generic, :app1 do
257
303
  repository_name 'brownleej/test1'
@@ -82,6 +82,7 @@ describe RubyYacht::ServerType do
82
82
  end
83
83
 
84
84
  project = RubyYacht.configuration.projects.last
85
+ expect(RubyYacht::Project::DSL.new(:test).singleton_class.all_attributes).to include :app_type_test_foo
85
86
  expect(project.app_type_test_environment).to eq 'staging'
86
87
  expect(project.app_type_test_foo).to eq 'test value'
87
88
  end
@@ -99,6 +100,8 @@ describe RubyYacht::ServerType do
99
100
  end
100
101
  end
101
102
  end
103
+
104
+ expect(RubyYacht::App::DSL.new(:app_type_test, :test1).singleton_class.all_attributes).to include :app_type_test_bar
102
105
 
103
106
  project = RubyYacht.configuration.projects.last
104
107
  app = project.apps.last