dice_bag 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/dice_bag.rb ADDED
@@ -0,0 +1,4 @@
1
+ require_relative 'dice_bag/available_templates'
2
+ module DiceBag
3
+ require 'dice_bag/railtie' if defined?(Rails)
4
+ end
@@ -0,0 +1,45 @@
1
+ # This class returns all the templates we can generate in this particular project
2
+ module DiceBag
3
+
4
+ class AvailableTemplates
5
+
6
+ class << self
7
+
8
+ def template_checkers
9
+ @template_checkers ||= []
10
+ end
11
+
12
+ def inherited(base)
13
+ template_checkers << base
14
+ end
15
+
16
+ def all
17
+ #all the classes than inherit from us in the ruby runtime
18
+ available_templates = []
19
+
20
+ template_checkers.each do |checker|
21
+ checker.new.templates.each do |template|
22
+ available_templates.push( template)
23
+ end
24
+ end
25
+ available_templates
26
+ end
27
+
28
+ def template_filename_for(filename)
29
+ self.all.each do |template_filename|
30
+ if template_filename.include? filename
31
+ return template_filename
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ end
38
+
39
+ end
40
+
41
+
42
+ # we require the our own templates checker here, for other gems we just need
43
+ # them to inherit from DiceBag::AvailableTemplates and require the file
44
+ # If Ruby loads the file we can find the class in the object space and call it
45
+ require_relative 'templates/gems_checker'
@@ -0,0 +1,43 @@
1
+ require 'dice_bag/available_templates'
2
+ require 'dice_bag/project'
3
+ require 'dice_bag/config_file'
4
+ require 'dice_bag/template_file'
5
+ require 'dice_bag/default_template_file'
6
+
7
+ module DiceBag
8
+ #This class seems a candidate to be converted to Thor, the problem is that we need to run
9
+ #in the same process than the rake task so all the gems are loaded before dice_bag
10
+ #is called and dice_bag can find what software is used in this project
11
+ class Command
12
+
13
+ def write_all
14
+ Project.templates_to_generate.each do |template|
15
+ write(template)
16
+ end
17
+ end
18
+
19
+ def write(template_name)
20
+ template_file = TemplateFile.new(template_name)
21
+ template_file.assert_existence
22
+ config_file = ConfigFile.new(template_name)
23
+
24
+ template_file.create_file(config_file)
25
+ end
26
+
27
+
28
+ def generate_all_templates
29
+ AvailableTemplates.all.each do |template|
30
+ generate_template(template)
31
+ end
32
+ end
33
+
34
+ def generate_template(file)
35
+ default_template = DefaultTemplateFile.new(file)
36
+ default_template.assert_existence
37
+ project_template = TemplateFile.new(File.basename(file))
38
+
39
+ default_template.create_file(project_template)
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,14 @@
1
+ require 'dice_bag/dice_bag_file.rb'
2
+ require 'dice_bag/project'
3
+
4
+ #this class encapsulate a configuration file of the project
5
+ module DiceBag
6
+
7
+ class ConfigFile
8
+ include DiceBagFile
9
+ def initialize(name)
10
+ @filename = name.gsub('.local', '').gsub('.erb', '').gsub('.template','')
11
+ @file = @filename
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,40 @@
1
+ module DiceBag
2
+
3
+ # This class abstracts access to configuration values, to be used within ERB
4
+ # templates. Currently, the values are read from the environment, as per the
5
+ # Twelve-Factor App principles.
6
+ class Configuration
7
+
8
+ def initialize
9
+ @prefixed = Hash.new
10
+ end
11
+
12
+ # Returns a Configuration::PrefixedWithFallback using the given +prefix+.
13
+ #
14
+ def [](prefix)
15
+ @prefixed[prefix] ||= PrefixedWithFallback.new(prefix, self)
16
+ end
17
+
18
+ def method_missing(name)
19
+ ENV[name.to_s.upcase]
20
+ end
21
+
22
+ # This class acts like +Configuration+ but with a prefix applied to the
23
+ # environment variables used to provide values. This is useful for providing
24
+ # per-environment overrides to value in Rails projects. If the prefixed
25
+ # environment variable is not found, the class delegates to the provided
26
+ # fallback Configuration class, without the prefix.
27
+ #
28
+ class PrefixedWithFallback
29
+ def initialize(prefix, fallback)
30
+ @prefix = prefix.to_s.upcase
31
+ @fallback = fallback
32
+ end
33
+
34
+ def method_missing(name)
35
+ ENV["#{ @prefix }_#{ name.to_s.upcase }"] || @fallback.send(name)
36
+ end
37
+ end
38
+ end
39
+
40
+ end
@@ -0,0 +1,34 @@
1
+ require 'dice_bag/dice_bag_file'
2
+ require 'dice_bag/project'
3
+
4
+ require 'tempfile'
5
+
6
+ #This file encapsulate the template files Dicebag brings with itself
7
+ module DiceBag
8
+
9
+ class DefaultTemplateFile
10
+ include DiceBagFile
11
+
12
+ def initialize(name)
13
+ #if called from command line with only a name we search in all our templates for the file
14
+ if (File.dirname(name) == '.')
15
+ name = AvailableTemplates.template_filename_for(name)
16
+ end
17
+ @filename = File.basename(name)
18
+ @file = name
19
+ end
20
+
21
+ def create_file(template_file)
22
+ contents = read_template(@file)
23
+ template_file.write(contents)
24
+ puts "new template file generated in #{template_file.file}.
25
+ execute 'rake config:all' to get the corresponding configuration file."
26
+ end
27
+
28
+ def read_template(template)
29
+ # Some templates need the name of the project. We put a placeholder
30
+ # PROJECT_NAME there, it gets substituted by the real name of the project here
31
+ File.readlines(template).join.gsub("PROJECT_NAME", Project.name)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,59 @@
1
+ require 'dice_bag/version'
2
+
3
+ # This module contains common methods for all the type of files Dicebag knows about:
4
+ # configuration files, templates files in the project an templates files shipped with dicebag
5
+ module DiceBag
6
+ module DiceBagFile
7
+ attr_reader :file, :filename
8
+ @@force = false
9
+
10
+ def assert_existence
11
+ unless File.exists?(@file)
12
+ raise "File #{@file} not found. Configuration file not created"
13
+ end
14
+ end
15
+
16
+ def write(contents)
17
+ File.open(@file, 'w') do |file|
18
+ file.puts(contents)
19
+ end
20
+ end
21
+
22
+ def should_write?(new_contents)
23
+ #we always overwrite if we are inside an script or we are not development
24
+ return true if @@force || !$stdin.tty? || ENV['RAILS_ENV'] == 'test' || ENV['RAILS_ENV'] == 'production'
25
+ return true if !File.exists?(file)
26
+ return false if diff(file, new_contents).empty?
27
+
28
+ while true
29
+ puts "Overwrite #{file} ? Recommended: Yes. "
30
+ puts " [Y]es, [n]o, [a]ll files, [q]uit, [d]show diff"
31
+ answer = $stdin.gets.tap{|text| text.strip!.downcase! if text}
32
+ case answer
33
+ when 'y'
34
+ return true
35
+ when 'n'
36
+ return false
37
+ when 'a'
38
+ return @@force = true
39
+ when 'q'
40
+ exit
41
+ when 'd'
42
+ puts diff(file, new_contents)
43
+ else
44
+ return true
45
+ end
46
+ end
47
+ end
48
+
49
+ private
50
+ def diff(destination, content)
51
+ diff_cmd = ENV['RAILS_DIFF'] || 'diff -u'
52
+ Tempfile.open(File.basename(destination), File.dirname(destination)) do |temp|
53
+ temp.write content
54
+ temp.rewind
55
+ `#{diff_cmd} #{destination} #{temp.path}`.strip
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,27 @@
1
+ #This class encapsulate data about the project dice_bag got in
2
+ module DiceBag
3
+
4
+ class Project
5
+
6
+ DEFAULT_NAME = 'project'
7
+ def self.name
8
+ #TODO: how to do find the name of the project in no-rails environments?
9
+ defined?(Rails) ? Rails.application.class.parent_name.downcase : DEFAULT_NAME
10
+ end
11
+
12
+ def self.config_files(filename)
13
+ File.join(Dir.pwd, filename)
14
+ end
15
+
16
+ #local templates always takes preference over generated templates
17
+ def self.templates_to_generate
18
+ generated_templates = Dir[Project.config_files("**/config/*.erb")]
19
+ custom_templates = Dir[Project.config_files("**/config/*.erb.local")]
20
+ dotNetTemplates = Dir[Project.config_files("**/*.config.template")]
21
+ all_files = generated_templates + custom_templates
22
+ templates = all_files.delete_if {|file| custom_templates.include?(file + '.local')}
23
+ dotNetTemplates = dotNetTemplates.delete_if {|file| file.include?("/bin/")}
24
+ all_files + dotNetTemplates
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,7 @@
1
+ module DiceBag
2
+ class Railtie < Rails::Railtie
3
+ rake_tasks do
4
+ require 'dice_bag/tasks'
5
+ end
6
+ end
7
+ end
@@ -0,0 +1 @@
1
+ load 'dice_bag/tasks/config.rake'
@@ -0,0 +1,31 @@
1
+ require 'erb'
2
+ require 'dice_bag/command'
3
+
4
+ desc "Configure the application from the environment. It creates template files if you need and their config files"
5
+ task :config => ['config:generate_all', 'config:all']
6
+
7
+ namespace :config do
8
+ desc "Create all the files from their templates in config/"
9
+ task :all do
10
+ DiceBag::Command.new.write_all
11
+ end
12
+
13
+ desc "Recreate the file passed as parameter from its template"
14
+ task :file, :filename do |t, args|
15
+ filename = args[:filename]
16
+ raise "A filename needs to be provided" if filename.nil?
17
+ DiceBag::Command.new.write(filename)
18
+ end
19
+
20
+ desc "Generates all the template files needed by this project, named as the parameter."
21
+ task :generate_all do
22
+ DiceBag::Command.new.generate_all_templates
23
+ end
24
+
25
+ desc "Regenerate a given template"
26
+ task :generate, :filename do |t, args|
27
+ filename = args[:filename]
28
+ raise "A filename needs to be provided" if filename.nil?
29
+ DiceBag::Command.new.generate_template(filename)
30
+ end
31
+ end
@@ -0,0 +1,39 @@
1
+ require 'dice_bag/dice_bag_file.rb'
2
+ require 'dice_bag/template_helpers'
3
+ require 'dice_bag/configuration'
4
+ require 'dice_bag/warning'
5
+
6
+ #this class encapsulates the template files we will generate in the project
7
+ #using this gem
8
+ module DiceBag
9
+
10
+ class TemplateFile
11
+ include DiceBagFile
12
+
13
+ # Methods from this module need to be called directly from within the ERB
14
+ # templates.
15
+ include DiceBag::TemplateHelpers
16
+
17
+ def initialize(name)
18
+ @filename = File.basename(name)
19
+ @file = name
20
+ end
21
+
22
+ def create_file(config_file)
23
+ # By passing "<>" we're trimming trailing newlines on lines that are
24
+ # nothing but ERB blocks (see documentation). This is useful for files
25
+ # like mauth_key where we want to control newlines carefully.
26
+ template = ERB.new(File.read(@file), nil, "<>")
27
+
28
+ #templates expect a configured object
29
+ configured = Configuration.new
30
+ warning = Warning.new(@filename)
31
+ contents = template.result(binding)
32
+
33
+ return unless config_file.should_write?(contents)
34
+ config_file.write(contents)
35
+ puts "file #{config_file.file} created"
36
+ end
37
+
38
+ end
39
+ end
@@ -0,0 +1,8 @@
1
+ module DiceBag
2
+ module TemplateHelpers
3
+ def generate_private_key
4
+ require 'openssl'
5
+ OpenSSL::PKey::RSA.generate(2048)
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,11 @@
1
+ <%= warning.as_yaml_comment %>
2
+
3
+ development: &default
4
+ access_key_id: <%= configured.aws_access_key_id %>
5
+ secret_access_key: <%= configured.aws_secret_access_key %>
6
+
7
+ test:
8
+ <<: *default
9
+
10
+ production:
11
+ <<: *default
@@ -0,0 +1,20 @@
1
+ <%= warning.as_yaml_comment %>
2
+
3
+ # This example file is intended to be used by a the developer who would like
4
+ # use a locally running memcached. By default, though,
5
+ # config/environments/development.rb sets the cache_store to memory
6
+ # To enable this storage mechanism in development mode, consult the
7
+ # cache configuration code in config/environments/production.rb
8
+
9
+ development: &default
10
+ namespace: PROJECT_NAME
11
+ host: localhost
12
+ port: 11211
13
+ value_max_bytes: 52428800
14
+ compress: true
15
+
16
+ test:
17
+ <<: *default
18
+
19
+ production:
20
+ <<: *default
@@ -0,0 +1,16 @@
1
+ <%= warning.as_yaml_comment %>
2
+
3
+ <% [:development, :test, :production].each do |env| %>
4
+ <%= env %>:
5
+ adapter: <%= configured[env].database_driver || 'mysql2' %>
6
+ database: <%= configured[env].database_name || "PROJECT_NAME_#{env}" %>
7
+ username: <%= configured[env].database_username || 'root' %>
8
+ password: <%= configured[env].database_password %>
9
+ host: <%= configured[env].database_host || 'localhost' %>
10
+ pool: 5
11
+ timeout: 5000
12
+ encoding: utf8
13
+ reconnect: false
14
+ <% db_cert = configured[env].database_ssl_cert %>
15
+ <%= db_cert ? "sslca: #{db_cert}" : '' %>
16
+ <% end %>
@@ -0,0 +1,44 @@
1
+ # This module has the logic that decides what templates will be
2
+ # generated for this project.
3
+ # this file lives in the same directory than all the templates it
4
+ # provides logic for.
5
+ require 'dice_bag'
6
+
7
+ module DiceBag
8
+ class CommonTemplatesBag < AvailableTemplates
9
+
10
+ def templates
11
+ @needed_templates = []
12
+ configured = Configuration.new
13
+
14
+ if defined?(Dalli)
15
+ add_template('dalli.yml.erb')
16
+ end
17
+
18
+ if defined?(Mysql2)
19
+ add_template('database.yml.erb')
20
+ end
21
+
22
+ if defined?(AWS)
23
+ add_template('aws.yml.erb')
24
+ end
25
+
26
+ if configured.google_analytics_id
27
+ add_template('google_analytics.yml.erb')
28
+ end
29
+
30
+ if defined?(NewRelic)
31
+ add_template('newrelic.yml.erb')
32
+ end
33
+
34
+ @needed_templates
35
+ end
36
+
37
+ def add_template(file)
38
+ pwd = File.dirname(__FILE__)
39
+ @needed_templates.push(File.join(pwd, file))
40
+ end
41
+
42
+ end
43
+
44
+ end
@@ -0,0 +1,5 @@
1
+ <%= warning.as_yaml_comment %>
2
+
3
+ <% if configured.google_analytics_id %>
4
+ <%= "#{configured.rails_env}: #{configured.google_analytics_id}" %>
5
+ <% end %>
@@ -0,0 +1,37 @@
1
+ <%= warning.as_yaml_comment %>
2
+
3
+ common: &default_settings
4
+ license_key: <%= configured.newrelic_key || "12345" %>
5
+ app_name: <%= configured.domain || 'PROJECT_NAME' %>
6
+ enabled: <%= configured.enable_newrelic || false %>
7
+ log_level: <%= configured.new_relic_debug == 'true' ? 'debug' : 'info' %>
8
+ ssl: false
9
+ apdex_t: 0.5
10
+ capture_params: false
11
+ transaction_tracer:
12
+ enabled: true
13
+ transaction_threshold: apdex_f
14
+ record_sql: obfuscated
15
+ stack_trace_threshold: <%= configured.new_relic_debug == 'true' ? '0.1' : '0.5' %>
16
+ error_collector:
17
+ enabled: true
18
+ capture_source: true
19
+ ignore_errors: ActionController::RoutingError
20
+ background:
21
+ monitor_mode: true
22
+ app_name: <%= configured.domain || 'PROJECT_NAME' %>
23
+
24
+ development:
25
+ <<: *default_settings
26
+ enabled: false
27
+ developer: true
28
+
29
+ test:
30
+ <<: *default_settings
31
+ enabled: false
32
+ developer: false
33
+
34
+ production:
35
+ <<: *default_settings
36
+ enabled: true
37
+ developer: false
@@ -0,0 +1,3 @@
1
+ module DiceBag
2
+ VERSION = '0.4.0'
3
+ end
@@ -0,0 +1,30 @@
1
+ module DiceBag
2
+ class Warning
3
+
4
+ def initialize(template_filename)
5
+ @template_filename = template_filename
6
+ end
7
+
8
+ def as_ruby_comment
9
+ lines.map {|line| "# #{line}\n" }.join
10
+ end
11
+
12
+ alias :as_yaml_comment :as_ruby_comment
13
+
14
+ def as_xml_comment
15
+ ['<!--', lines, '-->'].flatten.join("\n")
16
+ end
17
+
18
+ protected
19
+
20
+ def lines
21
+ [
22
+ "WARNING! Do not modify this file directly. It was generated from the",
23
+ "'#@template_filename' template file.",
24
+ "",
25
+ "Use the rake config task to reconfigure. See the template file for",
26
+ "further guidance."
27
+ ]
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+ require 'dice_bag/project'
3
+
4
+ describe DiceBag::Project do
5
+ let(:project) { DiceBag::Project }
6
+
7
+ describe "#name" do
8
+ it "should give me a default name for non Rails apps" do
9
+ project.name.should == DiceBag::Project::DEFAULT_NAME
10
+ end
11
+ end
12
+
13
+ end
@@ -0,0 +1,12 @@
1
+ # Require this file using `require "spec_helper"` to ensure that it is only
2
+ # loaded once.
3
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
4
+ require 'dice_bag'
5
+ require 'rspec'
6
+
7
+ RSpec.configure do |config|
8
+ config.treat_symbols_as_metadata_keys_with_true_values = true
9
+ config.run_all_when_everything_filtered = true
10
+ config.filter_run :focus
11
+ config.order = 'random'
12
+ end
metadata ADDED
@@ -0,0 +1,83 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dice_bag
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Andrew Smith
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-02-14 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: &73429760 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *73429760
25
+ description:
26
+ email:
27
+ - asmith@mdsol.com
28
+ executables: []
29
+ extensions: []
30
+ extra_rdoc_files: []
31
+ files:
32
+ - lib/dice_bag.rb
33
+ - lib/dice_bag/warning.rb
34
+ - lib/dice_bag/command.rb
35
+ - lib/dice_bag/project.rb
36
+ - lib/dice_bag/default_template_file.rb
37
+ - lib/dice_bag/tasks.rb
38
+ - lib/dice_bag/template_file.rb
39
+ - lib/dice_bag/dice_bag_file.rb
40
+ - lib/dice_bag/config_file.rb
41
+ - lib/dice_bag/version.rb
42
+ - lib/dice_bag/templates/newrelic.yml.erb
43
+ - lib/dice_bag/templates/database.yml.erb
44
+ - lib/dice_bag/templates/gems_checker.rb
45
+ - lib/dice_bag/templates/google_analytics.yml.erb
46
+ - lib/dice_bag/templates/dalli.yml.erb
47
+ - lib/dice_bag/templates/aws.yml.erb
48
+ - lib/dice_bag/railtie.rb
49
+ - lib/dice_bag/configuration.rb
50
+ - lib/dice_bag/tasks/config.rake
51
+ - lib/dice_bag/available_templates.rb
52
+ - lib/dice_bag/template_helpers.rb
53
+ - spec/command_spec.rb
54
+ - spec/spec_helper.rb
55
+ homepage: https://github.com/mdsol/dice_bag
56
+ licenses: []
57
+ post_install_message:
58
+ rdoc_options: []
59
+ require_paths:
60
+ - lib
61
+ required_ruby_version: !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ! '>='
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ none: false
69
+ requirements:
70
+ - - ! '>='
71
+ - !ruby/object:Gem::Version
72
+ version: '0'
73
+ requirements: []
74
+ rubyforge_project:
75
+ rubygems_version: 1.8.15
76
+ signing_key:
77
+ specification_version: 3
78
+ summary: Dice Bag is a library of rake tasks for configuring web apps in the style
79
+ of The Twelve-Factor App. It also provides continuous integration tasks that rely
80
+ on the configuration tasks.
81
+ test_files:
82
+ - spec/command_spec.rb
83
+ - spec/spec_helper.rb