jun 0.1.0 → 0.3.1

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 (53) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +18 -15
  3. data/jun.gemspec +4 -4
  4. data/lib/jun/action_controller/base.rb +6 -10
  5. data/lib/jun/action_controller/callbacks.rb +46 -0
  6. data/lib/jun/action_controller/metal.rb +22 -0
  7. data/lib/jun/action_controller/redirecting.rb +17 -0
  8. data/lib/jun/action_controller/rendering.rb +0 -4
  9. data/lib/jun/action_dispatch/routing/mapper.rb +28 -3
  10. data/lib/jun/action_dispatch/routing/route_set.rb +49 -2
  11. data/lib/jun/action_dispatch/routing/welcome.html.erb +59 -0
  12. data/lib/jun/{active_record.rb → active_record/base.rb} +14 -3
  13. data/lib/jun/active_record/migration.rb +76 -0
  14. data/lib/jun/active_record/migrator.rb +80 -0
  15. data/lib/jun/active_record/persistence.rb +60 -0
  16. data/lib/jun/active_record/relation.rb +42 -0
  17. data/lib/jun/active_support/core_ext/array/access.rb +33 -0
  18. data/lib/jun/active_support/core_ext/array/conversion.rb +18 -0
  19. data/lib/jun/active_support/core_ext/hash/transformation.rb +29 -0
  20. data/lib/jun/active_support/core_ext/string/access.rb +42 -0
  21. data/lib/jun/active_support/{inflector.rb → core_ext/string/inflector.rb} +18 -0
  22. data/lib/jun/active_support/core_ext.rb +5 -0
  23. data/lib/jun/active_support/dependencies.rb +2 -0
  24. data/lib/jun/application.rb +31 -0
  25. data/lib/jun/cli/commands/db/create.rb +21 -1
  26. data/lib/jun/cli/commands/db/drop.rb +4 -1
  27. data/lib/jun/cli/commands/db/migrate.rb +1 -1
  28. data/lib/jun/cli/commands/db/rollback.rb +15 -0
  29. data/lib/jun/cli/commands/db/schema/dump.rb +24 -0
  30. data/lib/jun/cli/commands/db/schema/load.rb +43 -0
  31. data/lib/jun/cli/commands/db/seed.rb +19 -0
  32. data/lib/jun/cli/commands/generate/migration.rb +27 -0
  33. data/lib/jun/cli/commands/new.rb +7 -2
  34. data/lib/jun/cli/commands/server.rb +1 -1
  35. data/lib/jun/cli/generator_templates/migration.rb.erb +11 -0
  36. data/lib/jun/cli/{generators/new → generator_templates/new_app}/Gemfile.erb +0 -0
  37. data/lib/jun/cli/{generators/new → generator_templates/new_app}/README.md.erb +0 -0
  38. data/lib/jun/cli/{generators/new → generator_templates/new_app}/app/controllers/application_controller.rb.erb +0 -0
  39. data/lib/jun/cli/{generators/new → generator_templates/new_app}/app/helpers/application_helper.rb.erb +0 -0
  40. data/lib/jun/cli/generator_templates/new_app/app/models/application_record.rb.erb +4 -0
  41. data/lib/jun/cli/{generators/new → generator_templates/new_app}/app/views/layouts/application.html.erb.erb +0 -0
  42. data/lib/jun/cli/generator_templates/new_app/bin/console.erb +8 -0
  43. data/lib/jun/cli/generator_templates/new_app/config/application.rb.erb +12 -0
  44. data/lib/jun/cli/generator_templates/new_app/config/environment.rb.erb +7 -0
  45. data/lib/jun/cli/{generators/new → generator_templates/new_app}/config/routes.rb.erb +0 -0
  46. data/lib/jun/cli/{generators/new → generator_templates/new_app}/config.ru.erb +1 -2
  47. data/lib/jun/cli/generator_templates/new_app/db/seeds.rb.erb +9 -0
  48. data/lib/jun/cli.rb +5 -0
  49. data/lib/jun/version.rb +1 -1
  50. data/lib/jun.rb +13 -2
  51. metadata +48 -26
  52. data/lib/jun/cli/generators/new/config/application.rb.erb +0 -18
  53. data/lib/jun/cli/generators/new/db/app.db.erb +0 -0
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ class Relation
5
+ def initialize(klass)
6
+ @klass = klass
7
+ @where_clauses = []
8
+ end
9
+
10
+ def where!(condition)
11
+ clause = condition.is_a?(String) ? condition : condition.map { |k, v| "#{k} = #{v}" }.join(" AND ")
12
+ @where_clauses << clause
13
+
14
+ self
15
+ end
16
+
17
+ def where(condition)
18
+ clone.where!(condition)
19
+ end
20
+
21
+ def to_sql
22
+ sql = "SELECT * FROM #{@klass.table_name}"
23
+ sql += " WHERE #{@where_clauses.join(" AND ")}" if @where_clauses.any?
24
+
25
+ sql
26
+ end
27
+
28
+ def records
29
+ @records ||= @klass.find_by_sql(to_sql)
30
+ end
31
+
32
+ alias to_a records
33
+
34
+ def first
35
+ records.first
36
+ end
37
+
38
+ def each(&block)
39
+ records.each(&block)
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Array
4
+ # Returns the second element in the array.
5
+ def second
6
+ self[1]
7
+ end
8
+
9
+ # Returns the third element in the array.
10
+ def third
11
+ self[2]
12
+ end
13
+
14
+ # Returns the fourth element in the array.
15
+ def fourth
16
+ self[3]
17
+ end
18
+
19
+ # Returns the fifth element in the array.
20
+ def fifth
21
+ self[4]
22
+ end
23
+
24
+ # Returns the third-to-last element in the array.
25
+ def third_to_last
26
+ self[-3]
27
+ end
28
+
29
+ # Returns the second-to-last element in the array.
30
+ def second_to_last
31
+ self[-2]
32
+ end
33
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Array
4
+ # Converts the array to a comma-separated sentence.
5
+ #
6
+ # ["one", "two", "three"].to_sentence #=> "one, two and three"
7
+ # ["left", "right"].to_sentence(last_delimiter: "or") #=> "left or right"
8
+ # [].to_sentence #=> ""
9
+ #
10
+ # @param delimiter [String] the delimiter value for connecting array elements.
11
+ # @param last_delimiter [String] the connecting word for the last array element.
12
+ def to_sentence(delimiter: ", ", last_delimiter: "and")
13
+ return "" if none?
14
+ return self.first if one?
15
+
16
+ "#{self[0...-1].join(delimiter)} #{last_delimiter} #{self[-1]}"
17
+ end
18
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Hash
4
+ # Returns a new hash with all keys converted to strings.
5
+ #
6
+ # { name: "Tom", age: 50 }.stringify_keys #=> {"name"=>"Tom", "age"=>50}
7
+ def stringify_keys
8
+ transform_keys(&:to_s)
9
+ end
10
+
11
+ # Modifies the hash in place to convert all keys to strings.
12
+ # Same as +stringify_keys+ but modifies +self+.
13
+ def stringify_keys!
14
+ transform_keys!(&:to_s)
15
+ end
16
+
17
+ # Returns a new hash with all keys converted to symbols.
18
+ #
19
+ # { "name" => "Tom", "age" => 50 }.symbolize_keys #=> {:name=>"Tom", :age=>50 }
20
+ def symbolize_keys
21
+ transform_keys { |key| key.to_sym rescue key }
22
+ end
23
+
24
+ # Modifies the hash in place to convert all keys to symbols.
25
+ # Same as +symbolize_keys+ but modifies +self+.
26
+ def symbolize_keys!
27
+ transform_keys! { |key| key.to_sym rescue key }
28
+ end
29
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ class String
4
+ # Returns a character at the given integer of the string. The first character
5
+ # would be returned for index 0, the second at index 1, and onward. If a range
6
+ # is given, a substring conaining the characters within the range of the given
7
+ # indicies is returned. If a Regex is provided, the matching substring is returned.
8
+ #
9
+ # string = "smoki"
10
+ # string.at(0) # => "s"
11
+ # string.at(1..3) # => "mok"
12
+ # string.at(-2) # => "k"
13
+ # string.at(/oki/) # => "oki"
14
+ def at(position)
15
+ self[position]
16
+ end
17
+
18
+ # Returns a substring from the given position (index) to the end of the string.
19
+ # If the position is negative, the starting point is counted from the end of the string.
20
+ #
21
+ # string = "smoki"
22
+ # string.from(0) # => "smoki"
23
+ # string.from(3) # => "ki"
24
+ # string.from(-2) # => "ki"
25
+ def from(position)
26
+ self[position, length]
27
+ end
28
+
29
+ # Returns a substring from the beginning of the string to the given position (index).
30
+ # If the position is negative, the ending point is counted from the end of the string.
31
+ #
32
+ # string = "smoki"
33
+ # string.to(0) # => "s"
34
+ # string.to(3) # => "smok"
35
+ # string.to(-2) # => "smok"
36
+ def to(position)
37
+ position += size if position.negative?
38
+ position = -1 if position.negative?
39
+
40
+ self[0, position + 1]
41
+ end
42
+ end
@@ -68,6 +68,11 @@ module Inflector
68
68
  'equipment'
69
69
  ]
70
70
 
71
+ # Returns the plural form of the string.
72
+ #
73
+ # "task".pluralize #=> "tasks"
74
+ # "octopus".pluralize #=> "octopi"
75
+ # "fish".singularize #=> "fish"
71
76
  def pluralize
72
77
  return self if UNCHANGEABLE.include? self
73
78
 
@@ -82,6 +87,11 @@ module Inflector
82
87
  self.sub(pattern, replacement)
83
88
  end
84
89
 
90
+ # Returns the singular form of the string.
91
+ #
92
+ # "tasks".singularize #=> "task"
93
+ # "octopi".singularize #=> "octopus"
94
+ # "fish".singularize #=> "fish"
85
95
  def singularize
86
96
  return self if UNCHANGEABLE.include? self
87
97
 
@@ -96,6 +106,10 @@ module Inflector
96
106
  self.sub(pattern, replacement)
97
107
  end
98
108
 
109
+ # Converts a string to under_score format.
110
+ #
111
+ # "HelloThere".underscore #=> "hello_there"
112
+ # "Foo::BarBaz".underscore #=> "foo/bar_baz"
99
113
  def underscore
100
114
  gsub(/::/, '/').
101
115
  gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
@@ -104,6 +118,10 @@ module Inflector
104
118
  downcase
105
119
  end
106
120
 
121
+ # Converts a string to CamelCase format.
122
+ #
123
+ # "hello_there".camelize #=> "HelloThere"
124
+ # "foo/bar_baz".camelize #=> "Foo::BarBaz"
107
125
  def camelize
108
126
  sub(/^[a-z\d]*/) { |match| match.capitalize }.
109
127
  gsub(/(?:_|(\/))([a-z\d]*)/) { "#{$1}#{$2.capitalize}" }.
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ Dir[File.expand_path("core_ext/**/*.rb", __dir__)].sort.each do |filepath|
4
+ require_relative filepath
5
+ end
@@ -8,6 +8,8 @@ module Jun
8
8
  attr_accessor :autoload_paths
9
9
  self.autoload_paths = []
10
10
 
11
+ # Returns the filepath for a given filename if the file exists
12
+ # in one of the directories specified in +autoload_paths+.
11
13
  def find_file(filename)
12
14
  autoload_paths.each do |path|
13
15
  filepath = File.join(path, "#{filename}.rb")
@@ -14,5 +14,36 @@ module Jun
14
14
  def routes
15
15
  @routes ||= Jun::ActionDispatch::Routing::RouteSet.new
16
16
  end
17
+
18
+ # Initializes the Jun application.
19
+ # @return [Boolean] +true+ if successful, +false+ if already initialized.
20
+ def initialize!
21
+ return false if initialized? || Jun.application.nil?
22
+
23
+ # Add app/* directories to autoload paths.
24
+ Jun::ActiveSupport::Dependencies.autoload_paths += Jun.root.join("app").children
25
+
26
+ # Set up routes and make its helpers available to controllers & views.
27
+ require Jun.root.join("config/routes.rb")
28
+ url_helpers = Jun.application.routes.url_helpers
29
+ Jun::ActionController::Base.include(url_helpers)
30
+ Jun::ActionView::Base.include(url_helpers)
31
+
32
+ # Include all helpers in app/helpers directory.
33
+ Dir.glob(Jun.root.join("app/helpers/**/*.rb")).each do |filepath|
34
+ helper_class_name = File.basename(filepath, ".rb").camelize
35
+ helper_class = Object.const_get(helper_class_name)
36
+
37
+ Jun::ActionView::Base.include(helper_class)
38
+ end
39
+
40
+ @initialized = true
41
+ end
42
+
43
+ # Checks whether the Jun application has been initialized.
44
+ # @return [Boolean] +true+ if initialized, +false+ if not initialized.
45
+ def initialized?
46
+ !!@initialized
47
+ end
17
48
  end
18
49
  end
@@ -6,7 +6,27 @@ module Jun
6
6
  module DB
7
7
  class Create < Base
8
8
  def process(*args)
9
- puts "Creating database..."
9
+ db_filepath = Jun.root.join("db/app.db")
10
+
11
+ if File.exist?(db_filepath)
12
+ puts "Database already exists."
13
+ else
14
+ File.open(db_filepath, "w") {}
15
+ create_schema_migrations_table
16
+ puts "Created database in #{db_filepath}."
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ def create_schema_migrations_table
23
+ ActiveRecord::Base.connection.execute(
24
+ <<~SQL
25
+ CREATE TABLE IF NOT EXISTS schema_migrations (
26
+ version text NOT NULL PRIMARY KEY
27
+ );"
28
+ SQL
29
+ )
10
30
  end
11
31
  end
12
32
  end
@@ -6,7 +6,10 @@ module Jun
6
6
  module DB
7
7
  class Drop < Base
8
8
  def process(*args)
9
- puts "Dropping database..."
9
+ db_filepath = Jun.root.join("db/app.db")
10
+
11
+ File.delete(db_filepath) if File.exist?(db_filepath)
12
+ puts "Dropped database."
10
13
  end
11
14
  end
12
15
  end
@@ -6,7 +6,7 @@ module Jun
6
6
  module DB
7
7
  class Migrate < Base
8
8
  def process(*args)
9
- puts "Running migrations..."
9
+ ActiveRecord::Migrator.new(direction: :up).call
10
10
  end
11
11
  end
12
12
  end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jun
4
+ module CLI
5
+ module Commands
6
+ module DB
7
+ class Rollback < Base
8
+ def process(*args)
9
+ ActiveRecord::Migrator.new(direction: :down).call
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jun
4
+ module CLI
5
+ module Commands
6
+ module DB
7
+ module Schema
8
+ class Dump < Base
9
+ def process(*args)
10
+ schema_filepath = Jun.root.join("db/schema.sql")
11
+ db_filepath = Jun.root.join("db/app.db")
12
+
13
+ Bundler.with_original_env do
14
+ system("bundle exec sqlite3 #{db_filepath} .schema > #{schema_filepath}")
15
+ end
16
+
17
+ puts "Database schema updated."
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jun
4
+ module CLI
5
+ module Commands
6
+ module DB
7
+ module Schema
8
+ class Load < Base
9
+ def process(*args)
10
+ schema_filepath = Jun.root.join("db/schema.sql")
11
+ db_filepath = Jun.root.join("db/app.db")
12
+
13
+ Bundler.with_original_env do
14
+ system("bundle exec sqlite3 #{db_filepath} < #{schema_filepath}")
15
+ end
16
+
17
+ populate_schema_migrations!
18
+
19
+ puts "Database schema loaded."
20
+ end
21
+
22
+ private
23
+
24
+ def populate_schema_migrations!
25
+ migration_files = Dir.glob(Jun.root.join("db/migrate/*.rb")).sort
26
+
27
+ migration_files.each do |filepath|
28
+ filename = filepath.split("/").last.sub(".rb", "")
29
+ migration_version = filename.split("_").first
30
+
31
+ add_to_schema_migrations(migration_version)
32
+ end
33
+ end
34
+
35
+ def add_to_schema_migrations(version)
36
+ ActiveRecord::Base.connection.execute("INSERT INTO schema_migrations (version) VALUES (#{version});")
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jun
4
+ module CLI
5
+ module Commands
6
+ module DB
7
+ class Seed < Base
8
+ def process(*args)
9
+ seed_filepath = Jun.root.join("db/seed.rb")
10
+ abort("No seed file found.") unless File.exist?(seed_filepath)
11
+
12
+ load seed_filepath
13
+ puts "Seeding complete."
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jun
4
+ module CLI
5
+ module Commands
6
+ module Generate
7
+ class Migration < Base
8
+ def process(*args)
9
+ migration_name = args.first
10
+
11
+ template_filepath = File.expand_path("../../generator_templates/migration.rb.erb", __dir__)
12
+ template = Tilt::ERBTemplate.new(template_filepath)
13
+ template_locals = { migration_name: migration_name }
14
+
15
+ filename = "#{Time.now.to_i}_#{migration_name}.rb"
16
+ filepath = Jun.root.join("db/migrate/#{filename}")
17
+ file_body = template.render(nil, template_locals)
18
+
19
+ filepath.dirname.mkpath
20
+ File.open(filepath, "w") { |f| f.write(file_body) }
21
+ puts "created #{filename}"
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -26,17 +26,20 @@ module Jun
26
26
  "README.md",
27
27
  "app/controllers/application_controller.rb",
28
28
  "app/helpers/application_helper.rb",
29
+ "app/models/application_record.rb",
29
30
  "app/views/layouts/application.html.erb",
31
+ "bin/console",
30
32
  "config/application.rb",
33
+ "config/environment.rb",
31
34
  "config/routes.rb",
32
- "db/app.db"
35
+ "db/seeds.rb"
33
36
  ]
34
37
 
35
38
  FileUtils.mkdir_p(app_name)
36
39
 
37
40
  FileUtils.chdir(app_name) do
38
41
  templates.each do |filepath|
39
- template_filepath = File.expand_path("../generators/new/#{filepath}.erb", __dir__)
42
+ template_filepath = File.expand_path("../generator_templates/new_app/#{filepath}.erb", __dir__)
40
43
  template = Tilt::ERBTemplate.new(template_filepath)
41
44
  template_locals = { app_name: app_name }
42
45
  file_body = template.render(nil, template_locals)
@@ -47,6 +50,8 @@ module Jun
47
50
  puts "created #{filepath}"
48
51
  end
49
52
 
53
+ FileUtils.chmod("u+x", "bin/console")
54
+
50
55
  puts "Installing dependencies..."
51
56
  Bundler.with_original_env { system("bundle install") }
52
57
  end
@@ -6,7 +6,7 @@ module Jun
6
6
  class Server < Base
7
7
  def process(*args)
8
8
  if Jun.root
9
- system("rerun --background -- rackup -p 3001")
9
+ system("rerun --background -- rackup -p 6291")
10
10
  else
11
11
  abort("Command \"#{self.class.command_name}\" must be run inside of a Jun app.")
12
12
  end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ class <%= migration_name.camelize %> < ActiveRecord::Migration
4
+ def up
5
+
6
+ end
7
+
8
+ def down
9
+
10
+ end
11
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ApplicationRecord < ActiveRecord::Base
4
+ end
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "jun"
6
+
7
+ require "irb"
8
+ IRB.start(__FILE__)
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "jun"
4
+
5
+ # Require gems listed in the Gemfile, including any gems specified
6
+ # in the current environment group (e.g. :development, :production).
7
+ Bundler.require(*Jun.groups)
8
+
9
+ module <%= app_name.camelize %>
10
+ class Application < Jun::Application
11
+ end
12
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Load the Jun application.
4
+ require_relative "application"
5
+
6
+ # Initialize the application.
7
+ Jun.application.initialize!
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "bundler/setup"
4
- require_relative "config/application"
5
- require_relative "config/routes"
4
+ require_relative "config/environment"
6
5
 
7
6
  run Jun.application
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file is used by the `jun db:seed` command and should include
4
+ # any necessary logic for seeding the database.
5
+ #
6
+ # Example:
7
+ #
8
+ # User.create(name: "Sam", occupation: "author")
9
+ # Post.create(title: "How to Build a Bird House")
data/lib/jun/cli.rb CHANGED
@@ -11,7 +11,12 @@ module Jun
11
11
  Jun::CLI::Commands::New,
12
12
  Jun::CLI::Commands::DB::Create,
13
13
  Jun::CLI::Commands::DB::Migrate,
14
+ Jun::CLI::Commands::DB::Rollback,
15
+ Jun::CLI::Commands::DB::Seed,
14
16
  Jun::CLI::Commands::DB::Drop,
17
+ Jun::CLI::Commands::DB::Schema::Dump,
18
+ Jun::CLI::Commands::DB::Schema::Load,
19
+ Jun::CLI::Commands::Generate::Migration,
15
20
  Jun::CLI::Commands::Server,
16
21
  Jun::CLI::Commands::Version
17
22
  ].freeze
data/lib/jun/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Jun
4
- VERSION = "0.1.0"
4
+ VERSION = "0.3.1"
5
5
  end
data/lib/jun.rb CHANGED
@@ -3,10 +3,13 @@
3
3
  require "pathname"
4
4
 
5
5
  require_relative "jun/version"
6
- require_relative "jun/active_support/inflector"
6
+ require_relative "jun/active_support/core_ext"
7
7
  require_relative "jun/active_support/dependencies"
8
8
  require_relative "jun/action_dispatch/routing/route_set"
9
- require_relative "jun/active_record"
9
+ require_relative "jun/active_record/base"
10
+ require_relative "jun/active_record/migration"
11
+ require_relative "jun/active_record/migrator"
12
+ require_relative "jun/active_record/relation"
10
13
  require_relative "jun/action_controller/base"
11
14
  require_relative "jun/application"
12
15
 
@@ -22,6 +25,14 @@ module Jun
22
25
  project_root_path
23
26
  end
24
27
 
28
+ def env
29
+ ENV["JUN_ENV"] || ENV["RACK_ENV"] || "development"
30
+ end
31
+
32
+ def groups
33
+ [:default, env]
34
+ end
35
+
25
36
  private
26
37
 
27
38
  def project_root_path