rails-infrastructure 0.1.0

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