rails-infrastructure 0.1.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 (41) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.ruby-gemset +1 -0
  4. data/.ruby-version +1 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.rdoc +80 -0
  8. data/Rakefile +7 -0
  9. data/examples/infrastructure/databases.yml +29 -0
  10. data/examples/infrastructure/environment.yml +2 -0
  11. data/examples/infrastructure/server.yml +9 -0
  12. data/examples/infrastructure/servers/unicorn.rb +69 -0
  13. data/examples/infrastructure/servers/unicorn.yml.erb +13 -0
  14. data/examples/infrastructure/version.yml +2 -0
  15. data/examples/infrastructure.yml +11 -0
  16. data/lib/infrastructure/configurable.rb +51 -0
  17. data/lib/infrastructure/configuration.rb +93 -0
  18. data/lib/infrastructure/constants.rb +5 -0
  19. data/lib/infrastructure/databases.rb +117 -0
  20. data/lib/infrastructure/environment.rb +16 -0
  21. data/lib/infrastructure/path.rb +7 -0
  22. data/lib/infrastructure/railtie.rb +68 -0
  23. data/lib/infrastructure/server.rb +7 -0
  24. data/lib/infrastructure/version.rb +23 -0
  25. data/lib/infrastructure.rb +25 -0
  26. data/lib/rails-infrastructure.rb +2 -0
  27. data/lib/tasks/databases.rake +148 -0
  28. data/lib/tasks/environment.rake +19 -0
  29. data/lib/tasks/server.rake +73 -0
  30. data/lib/tasks/version.rake +23 -0
  31. data/rails-infrastructure.gemspec +28 -0
  32. data/test/infrastructure/configurable_test.rb +94 -0
  33. data/test/infrastructure/configuration_test.yml.erb +5 -0
  34. data/test/infrastructure/database_test.rb +108 -0
  35. data/test/infrastructure/environment_test.rb +23 -0
  36. data/test/infrastructure/paths_test.rb +9 -0
  37. data/test/infrastructure/server_test.rb +9 -0
  38. data/test/infrastructure/version_test.rb +23 -0
  39. data/test/infrastructure_test.rb +26 -0
  40. data/test/test_helper.rb +2 -0
  41. metadata +163 -0
@@ -0,0 +1,5 @@
1
+ class Infrastructure
2
+
3
+ VERSION = '0.1.0'
4
+
5
+ end
@@ -0,0 +1,117 @@
1
+ class Infrastructure
2
+ class Databases
3
+
4
+ include Configurable
5
+
6
+ attr_reader :orm, :environment
7
+ attr_reader :global_defaults, :system
8
+
9
+ def initialize(orm, environment)
10
+ @orm = orm
11
+ @environment = environment.to_sym
12
+ @global_defaults = {}
13
+ @environment_defaults = {}
14
+ @system = {}
15
+ callback :after_configure, -> { merge_defaults }
16
+ callback :after_assignment, -> (key, value) { merge_defaults }
17
+ end
18
+
19
+ def merge_defaults
20
+ @global_defaults = @global_defaults.merge(configuration.fetch(:defaults) || {})
21
+ configuration.delete :defaults
22
+ @system = @system.merge(configuration.fetch(:system) || {}).merge @global_defaults
23
+ configuration.delete :system
24
+ configuration.each do |environment, configs|
25
+ @environment_defaults[environment] ||= Hash.new
26
+ @environment_defaults[environment] = @environment_defaults[environment].merge(configs.fetch(:defaults) || {})
27
+ configs.delete :defaults
28
+ configs.each do |id, config|
29
+ config.assign :database, "#{environment}_#{id}" unless config.fetch :database
30
+ config.assign :id, id.to_s unless config.fetch(:id) == id.to_s
31
+ @environment_defaults[environment].each { |k,v| config.assign k, v unless config.fetch k }
32
+ @global_defaults.each { |k,v| config.assign k, v unless config.fetch k }
33
+ end
34
+ end
35
+ end
36
+
37
+ def available?(id)
38
+ available.include? database(id)
39
+ end
40
+
41
+ def available
42
+ connection.execute("SELECT datname FROM pg_database").map { |result| result['datname'] }
43
+ end
44
+
45
+ def connection
46
+ orm.connection
47
+ end
48
+
49
+ def tables
50
+ connection.tables
51
+ end
52
+
53
+ def environment_configs(environment = environment)
54
+ fetch(environment) || raise(ArgumentError, "database configurations for environment #{environment} are not configured")
55
+ end
56
+ alias_method :configs, :environment_configs
57
+
58
+ def environment_database(id)
59
+ config(id)[:database]
60
+ end
61
+ alias_method :database, :environment_database
62
+
63
+ def environment_defaults(environment = environment)
64
+ @environment_defaults[environment] ||= Hash.new
65
+ end
66
+ alias_method :defaults, :environment_defaults
67
+
68
+ def environment_config(id)
69
+ if id.is_a?(String) || id.is_a?(Symbol)
70
+ id = id.to_sym
71
+ if config = configs.fetch(id)
72
+ return config.to_h
73
+ end
74
+ if id.to_s == defaults[:id]
75
+ if config = configs.fetch(defaults[:id])
76
+ return config.to_h
77
+ else
78
+ raise ArgumentError, "default database #{defaults[:id]} in environment #{environment} is not configured"
79
+ end
80
+ end
81
+ return system if id == :system
82
+ elsif id.is_a? Hash
83
+ return id
84
+ end
85
+ raise ArgumentError, "database configuration for #{id} in environment #{environment} is not configured"
86
+ end
87
+ alias_method :config, :environment_config
88
+
89
+ def drop(id)
90
+ connection.drop_database database(id)
91
+ end
92
+
93
+ def create(id)
94
+ connection.create_database database(id)
95
+ end
96
+
97
+ def connect(id)
98
+ orm.establish_connection config(id)
99
+ end
100
+
101
+ def connect!(id)
102
+ begin
103
+ connect id
104
+ rescue StandardError
105
+ connect system
106
+ create id
107
+ connect id
108
+ end
109
+ end
110
+
111
+ def databases(environment = environment)
112
+ configs(environment).map { |id, configs| configs[:database] }.compact.uniq.sort
113
+ end
114
+
115
+ end
116
+ end
117
+
@@ -0,0 +1,16 @@
1
+ class Infrastructure
2
+ class Environment
3
+
4
+ include Configurable
5
+
6
+ %w( production development test ).each do |environment|
7
+ define_method "#{environment}?", -> { to_s == environment }
8
+ end
9
+
10
+ def to_s
11
+ configuration.fetch(:environment).to_s
12
+ end
13
+
14
+ end
15
+ end
16
+
@@ -0,0 +1,7 @@
1
+ class Infrastructure
2
+ class Path
3
+
4
+ include Configurable
5
+
6
+ end
7
+ end
@@ -0,0 +1,68 @@
1
+ unless defined? ::Inf
2
+ ::Inf = Infrastructure.new
3
+ end
4
+
5
+ class Infrastructure
6
+ class Railtie < Rails::Railtie
7
+
8
+ config.before_configuration do |app|
9
+
10
+ Inf.build :path, Path
11
+ Inf.path.assign :rails, Hash.new
12
+ Inf.path.rails.configure do
13
+ assign :root, Dir.pwd
14
+ Dir.glob("#{root}/*").select { |f| File.directory? f }.each do |dir|
15
+ dir = File.basename(dir)
16
+ assign "#{dir}_dir", root + "/#{dir}"
17
+ Dir.glob("#{root}/#{dir}/*").select { |f| File.directory? f }.each do |sub_dir|
18
+ sub_dir = File.basename(sub_dir)
19
+ assign "#{dir}_#{sub_dir}_dir", root + "/#{dir}/#{sub_dir}"
20
+ end
21
+ end
22
+ Dir.glob("#{root}/config/infrastructure/*").select { |f| File.directory? f }.each do |dir|
23
+ dir = File.basename(dir)
24
+ assign "infrastructure_#{dir}_dir", root + "/config/infrastructure/#{dir}"
25
+ end
26
+ end
27
+
28
+ Inf.load_config_if_exists Inf.path.rails.config_dir + '/infrastructure.yml'
29
+ Inf.load_config_if_exists Inf.path.rails.config_dir + '/infrastructure.yml.erb'
30
+
31
+ Inf.build :environment, Environment
32
+ Inf.environment.load_config_if_exists Inf.path.rails.config_infrastructure_dir + '/environment.yml' do
33
+ ENV['RAILS_ENV'] = Inf.environment.to_s
34
+ Rails.env = Inf.environment.to_s
35
+ rake_tasks { load 'tasks/environment.rake' }
36
+ end
37
+
38
+ Inf.build :version, Version
39
+ Inf.version.load_config_if_exists Inf.path.rails.config_infrastructure_dir + '/version.yml' do
40
+ rake_tasks { load 'tasks/version.rake' }
41
+ end
42
+
43
+ Inf.build :server, Server
44
+ Inf.server.load_config_if_exists Inf.path.rails.config_infrastructure_dir + '/server.yml' do
45
+ rake_tasks { load 'tasks/server.rake' }
46
+ Inf.server.servers.each do |server|
47
+ Inf.server.assign server, Hash.new
48
+ Inf.server.fetch(server).assign Inf.server.fetch(:defaults).to_h
49
+ %w( .yml .yml.erb ).each do |extension|
50
+ Inf.server.fetch(server).load_config_if_exists Inf.path.rails.infrastructure_servers_dir + "/#{server}" + extension
51
+ end
52
+ end
53
+ end
54
+
55
+ end
56
+
57
+ config.before_initialize do
58
+ Inf.build :databases, Databases, ActiveRecord::Base, Rails.env
59
+ Inf.databases.load_config_if_exists Inf.path.rails.config_infrastructure_dir + '/databases.yml' do
60
+ ENV['DATABASE_URL'] = Inf.databases.environment_defaults[:id]
61
+ Rails::Application::Configuration.send(:define_method, :database_configuration) { Inf.databases.environment_configs }
62
+ ActiveRecord::Migration.verbose = false
63
+ rake_tasks { load 'tasks/databases.rake' }
64
+ end
65
+ end
66
+
67
+ end
68
+ end
@@ -0,0 +1,7 @@
1
+ class Infrastructure
2
+ class Server
3
+
4
+ include Configurable
5
+
6
+ end
7
+ end
@@ -0,0 +1,23 @@
1
+ class Infrastructure
2
+
3
+ class Version
4
+
5
+ include Configurable
6
+
7
+ def update_version(i, value = nil)
8
+ array_version = configuration.version.split('.')
9
+ array_version[i] = value || (array_version[i] || 0).to_i + 1
10
+ assign :version, array_version.join('.')
11
+ end
12
+
13
+ %w( major minor tiny ).each_with_index do |version, index|
14
+ define_method "bump_#{version}", -> { update_version index }
15
+ end
16
+
17
+ def to_s
18
+ version.to_s
19
+ end
20
+
21
+ end
22
+ end
23
+
@@ -0,0 +1,25 @@
1
+ require 'forwardable'
2
+ require 'erb'
3
+ require 'yaml'
4
+
5
+ require 'colored'
6
+
7
+ require 'infrastructure/constants'
8
+ require 'infrastructure/configurable'
9
+ require 'infrastructure/configuration'
10
+ require 'infrastructure/environment'
11
+ require 'infrastructure/version'
12
+ require 'infrastructure/path'
13
+ require 'infrastructure/databases'
14
+ require 'infrastructure/server'
15
+
16
+ class Infrastructure
17
+
18
+ include Configurable
19
+
20
+ def build(key, model, *params)
21
+ model = model.constantize if model.is_a? String
22
+ assign key, model.new(*params)
23
+ end
24
+
25
+ end
@@ -0,0 +1,2 @@
1
+ require 'infrastructure'
2
+ require 'infrastructure/railtie' if defined? Rails
@@ -0,0 +1,148 @@
1
+ def forced?(params = {})
2
+ @forced = true if params[:force] == 'force'
3
+ @forced == true
4
+ end
5
+
6
+ def exit_if_production(params = {})
7
+ if Rails.env.production? && ! forced?(params)
8
+ puts 'This task must be forced in production environment ('.yellow + 'rake <task>[force]'.red + ')'.yellow
9
+ exit!
10
+ end
11
+ end
12
+
13
+ module Rake
14
+ class Task
15
+ def self.reinvoke(*params, &block)
16
+ invoke(*params, &block)
17
+ reenable()
18
+ end
19
+ end
20
+ end
21
+
22
+ namespace :database do
23
+
24
+ desc "Create database if it does not exist"
25
+ task :create, :id do |name, params|
26
+ puts 'Creating: ' + params[:id].to_s.green
27
+ Inf.databases.connect :super
28
+ Inf.databases.create params[:id] if ! Inf.databases.available? params[:id]
29
+ end
30
+
31
+ desc "Drop database if exists"
32
+ task :drop, :id, :force do |name, params|
33
+ exit_if_production params
34
+ puts 'Dropping: ' + params[:id].to_s.green
35
+ Inf.databases.connect :super
36
+ Inf.databases.drop params[:id] if Inf.databases.available? params[:id]
37
+ end
38
+
39
+ desc "Migrate database"
40
+ task :migrate, :id, :version do |name, params|
41
+ puts 'Migrating: ' + params[:id].to_s.green
42
+ Inf.databases.connect params[:id]
43
+ ActiveRecord::Base.logger.level = Logger::INFO
44
+ current_version = ActiveRecord::Migrator.current_version
45
+ target_version = (params[:version] || ActiveRecord::Migrator.last_version).to_i
46
+ ActiveRecord::Migrator.migrate ActiveRecord::Migrator.migrations_paths, target_version
47
+ ActiveRecord::Migrator.migrations(ActiveRecord::Migrator.migrations_paths).each do |migration|
48
+ if target_version < current_version
49
+ puts "Rollbacked: - #{migration.version} - #{migration.name.green} - #{migration.filename}" if migration.version >= target_version && migration.version < current_version
50
+ else
51
+ puts "Migrated: - #{migration.version} - #{migration.name.green} - #{migration.filename}" if migration.version > current_version && migration.version <= target_version
52
+ end
53
+ end
54
+ end
55
+
56
+ desc "Rollback database"
57
+ task :rollback, :id, :version do |name, params|
58
+ puts 'Rollbacking: ' + params[:id].to_s.green
59
+ Inf.databases.connect params[:id]
60
+ ActiveRecord::Base.logger.level = Logger::INFO
61
+ current_version = ActiveRecord::Migrator.current_version
62
+ all_versions = ActiveRecord::Migrator.get_all_versions
63
+ previous_index = all_versions.index(current_version) - 1
64
+ previous_index = 0 if previous_index < 0
65
+ previous_version = all_versions[previous_index]
66
+ target_version = (params[:version] || previous_version).to_i
67
+ ActiveRecord::Migrator.migrate ActiveRecord::Migrator.migrations_paths, target_version
68
+ ActiveRecord::Migrator.migrations(ActiveRecord::Migrator.migrations_paths).each do |migration|
69
+ if target_version < current_version
70
+ puts "Rollbacked: - #{migration.version} - #{migration.name.green} - #{migration.filename}" if migration.version >= target_version && migration.version < current_version
71
+ else
72
+ puts "Migrated: - #{migration.version} - #{migration.name.green} - #{migration.filename}" if migration.version > current_version && migration.version <= target_version
73
+ end
74
+ end
75
+ end
76
+
77
+ desc "Seed database"
78
+ task :seed, :id do |name, params|
79
+ exit_if_production params
80
+ puts 'Seeding: ' + params[:id].to_s.green
81
+ Inf.databases.connect params[:id]
82
+ Rake::Task['database:migrate'].reinvoke params[:id]
83
+ load Inf.path.rails.db_dir + 'seeds.rb'
84
+ end
85
+
86
+ desc "Drop, create, then migrate database"
87
+ task :reset, :id, :force do |name, params|
88
+ exit_if_production params
89
+ Rake::Task['database:drop'].reinvoke params[:id]
90
+ Rake::Task['database:create'].reinvoke params[:id]
91
+ Rake::Task['database:migrate'].reinvoke params[:id]
92
+ end
93
+
94
+ namespace :reset do
95
+ desc "Reset then seed database"
96
+ task :seed, :force do |name, params|
97
+ exit_if_production params
98
+ Rake::Task['databases:reset'].reinvoke params[:id], params
99
+ Rake::Task['databases:seed'].reinvoke params[:id]
100
+ end
101
+ end
102
+
103
+ end
104
+
105
+ namespace :databases do
106
+
107
+ desc "Create all databases that dont exist"
108
+ task :create do
109
+ Inf.databases.configs.each { |id, config| Rake::Task['database:create'].reinvoke id }
110
+ end
111
+
112
+ desc "Drop all databases that exist"
113
+ task :drop, :force do |name, params|
114
+ exit_if_production params
115
+ Inf.databases.configs.each { |id, config| Rake::Task['database:drop'].reinvoke id }
116
+ end
117
+
118
+ desc "Migrate all databases"
119
+ task :migrate, :version do |name, params|
120
+ Inf.databases.configs.each { |id, config| Rake::Task['database:migrate'].reinvoke id, params[:version] }
121
+ end
122
+
123
+ desc "Rollback all databases"
124
+ task :rollback, :version do |name, params|
125
+ Inf.databases.configs.each { |id, config| Rake::Task['database:rollback'].reinvoke id }
126
+ end
127
+
128
+ desc "Drop, create, migrate all databases"
129
+ task :reset, :force do |name, params|
130
+ exit_if_production params
131
+ Inf.databases.configs.each { |id, config| Rake::Task['database:reset'].reinvoke id }
132
+ end
133
+
134
+ desc "Seed all databases"
135
+ task :seed, :force do |name, params|
136
+ exit_if_production params
137
+ Inf.databases.configs.each { |id, config| Rake::Task['database:seed'].reinvoke id }
138
+ end
139
+
140
+ namespace :reset do
141
+ desc "Reset then seed all databases"
142
+ task :seed, :force do |name, params|
143
+ exit_if_production params
144
+ Inf.databases.configs.each { |id, config| Rake::Task['database:seed'].reinvoke id, params[:force] }
145
+ end
146
+ end
147
+
148
+ end
@@ -0,0 +1,19 @@
1
+ namespace :environment do
2
+
3
+ desc 'display current environment'
4
+ task :current do
5
+ puts 'Environment: ' + Inf.environment.to_s.green
6
+ end
7
+
8
+ def set_environment_and_save(environment)
9
+ Inf.environment.assign :environment, environment
10
+ Inf.environment.configuration.save_config Inf.path.rails.config_infrastructure_dir + '/environment.yml'
11
+ Rake::Task['environment:current'].invoke
12
+ end
13
+
14
+ %w( test development production ).each do |environment|
15
+ desc "Change to #{environment} environment"
16
+ task(environment.to_sym) { set_environment_and_save environment }
17
+ end
18
+
19
+ end