heroku support + fix minor bugs
This commit is contained in:
parent
28211edad6
commit
29c22c05c1
1
.gitignore
vendored
1
.gitignore
vendored
@ -15,3 +15,4 @@ pkg
|
||||
rails_3_gems
|
||||
doc/performance.txt
|
||||
doc/production.sh
|
||||
*.gem
|
||||
|
1
Gemfile
1
Gemfile
@ -20,6 +20,7 @@ gem "mimetype-fu", :require => "mimetype_fu"
|
||||
gem "formtastic-rails3", :require => "formtastic"
|
||||
gem "carrierwave-rails3", :require => "carrierwave"
|
||||
gem "actionmailer-with-request", :require => 'actionmailer_with_request'
|
||||
gem "heroku"
|
||||
|
||||
# Development environment
|
||||
group :development do
|
||||
|
@ -5,7 +5,7 @@
|
||||
= f.foldable_inputs :name => :custom_fields, :class => 'editable-list fields' do
|
||||
- ordered_custom_fields.each do |field|
|
||||
= f.fields_for collection_name.to_sym, field, :child_index => field._index do |g|
|
||||
%li{ :class => "item added #{'error' unless field.errors.empty?}"}
|
||||
%li{ :class => "item added #{'new' if f.object.new_record?} #{'error' unless field.errors.empty?}"}
|
||||
%span.handle
|
||||
= image_tag 'admin/form/icons/drag.png'
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
|
||||
= g.hidden_field :hint, :class => 'hint'
|
||||
|
||||
= g.text_field :label
|
||||
= g.text_field :label, :class => 'label'
|
||||
|
||||
—
|
||||
|
||||
|
@ -8,9 +8,9 @@
|
||||
/ [if IE]
|
||||
= stylesheet_link_tag 'admin/blueprint/ie', :media => 'screen'
|
||||
|
||||
= stylesheet_link_tag 'admin/layout', 'admin/plugins/toggle', 'admin/menu', 'admin/buttons', 'admin/formtastic', 'admin/formtastic_changes', 'admin/application', :media => 'screen'
|
||||
= stylesheet_link_tag 'admin/layout', 'admin/plugins/toggle', 'admin/menu', 'admin/buttons', 'admin/formtastic', 'admin/formtastic_changes', 'admin/application', :media => 'screen', :cache => Rails.env.production? && !Locomotive.heroku?
|
||||
|
||||
= javascript_include_tag 'admin/jquery', 'admin/jquery.ui', 'admin/rails', 'admin/utils', 'admin/plugins/toggle', 'admin/plugins/growl', 'admin/plugins/cookie', 'admin/application'
|
||||
= javascript_include_tag 'admin/jquery', 'admin/jquery.ui', 'admin/rails', 'admin/utils', 'admin/plugins/toggle', 'admin/plugins/growl', 'admin/plugins/cookie', 'admin/application', :cache => Rails.env.production? && !Locomotive.heroku?
|
||||
|
||||
%script{ :type => 'text/javascript' }
|
||||
= find_and_preserve(growl_message)
|
||||
|
@ -4,7 +4,7 @@
|
||||
%head
|
||||
%title= escape_once("#{Locomotive.config.name} — #{current_site.name}")
|
||||
|
||||
= stylesheet_link_tag 'admin/blueprint/screen', 'admin/login', :media => 'screen'
|
||||
= stylesheet_link_tag 'admin/blueprint/screen', 'admin/login', :media => 'screen', :cache => Rails.env.production? && !Locomotive.config.heroku
|
||||
/ [if IE]
|
||||
= stylesheet_link_tag('admin/blueprint/ie', :media => 'screen')
|
||||
|
||||
|
@ -39,6 +39,8 @@ en:
|
||||
string: Simple Input
|
||||
text: Text
|
||||
category: Select
|
||||
edit_field:
|
||||
title: Edit field
|
||||
edit_category:
|
||||
title: Edit options
|
||||
help: Manage the list of options for your select box.
|
||||
|
11
doc/TODO
11
doc/TODO
@ -1,8 +1,7 @@
|
||||
BOARD:
|
||||
|
||||
- deploy on Heroku
|
||||
- observers to add / remove domains
|
||||
- "field name" for alias / hint for custom fields
|
||||
- observers to add / remove domains (http://groups.google.com/group/heroku/browse_thread/thread/148d6ea68e4574fb/4d8f1c8545d52bda?lnk=gst&q=heroku+gem+api#4d8f1c8545d52bda)
|
||||
|
||||
BACKLOG:
|
||||
|
||||
@ -23,8 +22,11 @@ BACKLOG:
|
||||
- refactor slugify method (use parameterize + create a module)
|
||||
- cucumber features for admin pages
|
||||
|
||||
- Heroku / S3 / Worker
|
||||
|
||||
BUGS:
|
||||
|
||||
- custom fields: accepts_nested_attributes weird behaviour when creating new content type + adding random fields
|
||||
|
||||
NICE TO HAVE:
|
||||
- asset collections: custom resizing if image
|
||||
@ -128,4 +130,7 @@ x upload files in S3
|
||||
x [BUG] asset vignette: name + icon not vertically aligned
|
||||
x truncate nom dans le menu de contents
|
||||
x site subdomain regexp [a-z][A-Z][0-9]
|
||||
! migrate content_instance to its own collection => http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24sliceoperator
|
||||
! migrate content_instance to its own collection => http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24sliceoperator
|
||||
x [BUG] "field name" for alias / hint for custom fields
|
||||
x [BUG] can not remove custom fields at creation if object is invalid
|
||||
x internal logger
|
@ -9,4 +9,18 @@ Locomotive.configure do |config|
|
||||
|
||||
# tell if logs are enabled. Useful for debug purpose.
|
||||
config.enable_logs = true
|
||||
|
||||
# tell if the application is hosted on Heroku.
|
||||
# Locomotive uses heroku api to add / remove domains.
|
||||
# there are 2 ways of passing heroku credentials to Locomotive
|
||||
# - from ENV variables: HEROKU_LOGIN & HEROKU_PASSWORD
|
||||
# - from this file
|
||||
#
|
||||
# Notes:
|
||||
# - IMPORTANT: behaviours related to this option will only be applied in production
|
||||
# - credentials coming from this file take precedence over ENV variables
|
||||
#
|
||||
# Ex:
|
||||
# config.heroku = { :name => '<my heroku app name>', :login => 'john@doe.net', :password => 'easy' }
|
||||
config.heroku = false
|
||||
end
|
@ -1,12 +1,16 @@
|
||||
# require 'locomotive/patches'
|
||||
require 'locomotive/configuration'
|
||||
require 'locomotive/logger'
|
||||
require 'locomotive/liquid'
|
||||
require 'locomotive/mongoid'
|
||||
require 'locomotive/heroku'
|
||||
|
||||
require 'mongo_session_store/mongoid'
|
||||
|
||||
module Locomotive
|
||||
|
||||
include Locomotive::Heroku
|
||||
|
||||
class << self
|
||||
attr_accessor :config
|
||||
|
||||
@ -26,12 +30,22 @@ module Locomotive
|
||||
def self.after_configure
|
||||
raise '[Error] Locomotive needs a default domain name' if Locomotive.config.default_domain.blank?
|
||||
|
||||
ActionMailer::Base.default_url_options[:host] = Locomotive.config.default_domain + (Rails.env.development? ? ':3000' : '')
|
||||
ActionMailer::Base.default_url_options[:host] = self.config.default_domain + (Rails.env.development? ? ':3000' : '')
|
||||
|
||||
# cookies stored in mongodb
|
||||
Rails.application.config.session_store :mongoid_store, {
|
||||
:key => Locomotive.config.cookie_key,
|
||||
:domain => ".#{Locomotive.config.default_domain}"
|
||||
}
|
||||
|
||||
# Heroku support
|
||||
self.enable_heroku if self.heroku?
|
||||
end
|
||||
|
||||
def self.logger(message)
|
||||
if Locomotive.config.enable_logs == true
|
||||
Rails.logger.info(message)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
@ -3,13 +3,14 @@ module Locomotive
|
||||
|
||||
@@defaults = {
|
||||
:name => 'LocomotiveApp',
|
||||
:default_domain => 'rails.local.fr',
|
||||
:default_domain => 'example.com',
|
||||
:reserved_subdomains => %w{www admin email blog webmail mail support help site sites},
|
||||
# :forbidden_paths => %w{layouts snippets stylesheets javascripts assets admin system api},
|
||||
:reserved_slugs => %w{stylesheets javascripts assets admin images api pages},
|
||||
:locales => %w{en fr},
|
||||
:cookie_key => '_locomotive_session',
|
||||
:enable_logs => false
|
||||
:enable_logs => false,
|
||||
:heroku => false
|
||||
}
|
||||
|
||||
cattr_accessor :settings
|
||||
|
59
lib/locomotive/heroku.rb
Normal file
59
lib/locomotive/heroku.rb
Normal file
@ -0,0 +1,59 @@
|
||||
require 'heroku'
|
||||
|
||||
module Locomotive
|
||||
module Heroku
|
||||
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
class << self
|
||||
attr_accessor :heroku_connection
|
||||
attr_accessor :heroku_domains
|
||||
end
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
|
||||
def heroku?
|
||||
!self.config.heroku.nil? && self.config.heroku.respond_to?(:[])
|
||||
end
|
||||
|
||||
def enable_heroku
|
||||
raise 'Heroku application name is mandatory' if self.config.heroku[:name].blank?
|
||||
|
||||
self.open_heroku_connection
|
||||
self.enhance_site_model
|
||||
|
||||
# "cache" domains for better performance
|
||||
self.heroku_domains = self.heroku_connection.list_domains(self.config.heroku[:name]).collect { |h| h[:domain] }
|
||||
end
|
||||
|
||||
def open_heroku_connection
|
||||
login = self.config.heroku[:login] || ENV['HEROKU_LOGIN']
|
||||
password = self.config.heroku[:password] || ENV['HEROKU_PASSWORD']
|
||||
|
||||
self.heroku_connection = ::Heroku::Client.new(login, password)
|
||||
end
|
||||
|
||||
def enhance_site_model
|
||||
Site.send :include, Locomotive::Heroku::CustomDomain
|
||||
end
|
||||
|
||||
# manage domains
|
||||
|
||||
def add_heroku_domain(name)
|
||||
Locomotive.logger "[add heroku domain] #{name}"
|
||||
self.heroku_connection.add_domain(self.config.heroku[:name], name)
|
||||
self.heroku_domains << name
|
||||
end
|
||||
|
||||
def remove_heroku_domain(name)
|
||||
Locomotive.logger "[remove heroku domain] #{name}"
|
||||
self.heroku_connection.remove_domain(self.config.heroku[:name], name)
|
||||
self.heroku_domains.delete(name)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
52
lib/locomotive/heroku/custom_domain.rb
Normal file
52
lib/locomotive/heroku/custom_domain.rb
Normal file
@ -0,0 +1,52 @@
|
||||
module Locomotive
|
||||
module Heroku
|
||||
module CustomDomain
|
||||
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
|
||||
after_save :add_heroku_domains
|
||||
after_destroy :remove_heroku_domains
|
||||
|
||||
alias_method_chain :add_subdomain_to_domains, :heroku
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
|
||||
protected
|
||||
|
||||
def add_subdomain_to_domains_with_heroku
|
||||
unless self.domains_change.nil?
|
||||
full_subdomain = "#{self.subdomain}.#{Locomotive.config.default_domain}"
|
||||
@heroku_domains_change = {
|
||||
:added => self.domains_change.last - self.domains_change.first - [full_subdomain],
|
||||
:removed => self.domains_change.first - self.domains_change.last - [full_subdomain]
|
||||
}
|
||||
end
|
||||
|
||||
add_subdomain_to_domains_without_heroku
|
||||
end
|
||||
|
||||
def add_heroku_domains
|
||||
return if @heroku_domains_change.nil?
|
||||
|
||||
@heroku_domains_change[:added].each do |name|
|
||||
Locomotive.add_heroku_domain(name)
|
||||
end
|
||||
@heroku_domains_change[:removed].each do |name|
|
||||
Locomotive.remove_heroku_domain(name)
|
||||
end
|
||||
end
|
||||
|
||||
def remove_heroku_domains
|
||||
self.domains_without_subdomain.each do |name|
|
||||
Locomotive.remove_heroku_domain(name)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
11
lib/locomotive/logger.rb
Normal file
11
lib/locomotive/logger.rb
Normal file
@ -0,0 +1,11 @@
|
||||
module Locomotive
|
||||
module Logger
|
||||
|
||||
def self.method_missing(meth, args, &block)
|
||||
if Locomotive.config.enable_logs == true
|
||||
Rails.logger.send(meth, args)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
@ -36,7 +36,6 @@ module Locomotive
|
||||
assigns = {
|
||||
'site' => current_site,
|
||||
'asset_collections' => Locomotive::Liquid::Drops::AssetCollections.new(current_site),
|
||||
# 'theme_assets' => Locomotive::Liquid::Drops::ThemeAssets.new(current_site),
|
||||
'stylesheets' => Locomotive::Liquid::Drops::Stylesheets.new(current_site),
|
||||
'javascripts' => Locomotive::Liquid::Drops::Javascripts.new(current_site),
|
||||
'contents' => Locomotive::Liquid::Drops::Contents.new(current_site),
|
||||
|
@ -19,7 +19,7 @@ module Locomotive
|
||||
protected
|
||||
|
||||
def fetch_site
|
||||
logger.info "[fetch site] host = #{request.host} / #{request.env['HTTP_HOST']}" if Locomotive.config.enable_logs
|
||||
Locomotive.logger "[fetch site] host = #{request.host} / #{request.env['HTTP_HOST']}"
|
||||
@current_site ||= Site.match_domain(request.host).first
|
||||
end
|
||||
|
||||
|
@ -44,6 +44,10 @@ $(document).ready(function() {
|
||||
|
||||
$('fieldset.fields li.template button').click(function() {
|
||||
var lastRow = $(this).parents('li.template');
|
||||
|
||||
var label = lastRow.find('input.label').val().trim();
|
||||
if (label == '' || label == defaultValue) return false;
|
||||
|
||||
var newRow = lastRow.clone(true).removeClass('template').addClass('added new').insertBefore(lastRow);
|
||||
|
||||
var dateFragment = '[' + new Date().getTime() + ']';
|
||||
@ -51,10 +55,6 @@ $(document).ready(function() {
|
||||
$(this).attr('name', $(this).attr('name').replace('[-1]', dateFragment));
|
||||
});
|
||||
|
||||
// should copy the value of the select box
|
||||
var input = newRow.find('input.label');
|
||||
if (lastRow.find('input.label').val() == '') input.val("undefined");
|
||||
|
||||
var select = newRow.find('select')
|
||||
.val(lastRow.find('select').val())
|
||||
.change(function() { selectOnChange(select); })
|
||||
@ -73,9 +73,8 @@ $(document).ready(function() {
|
||||
select.show();
|
||||
});
|
||||
|
||||
// then reset the form
|
||||
lastRow.find('input').val(defaultValue).addClass('void');
|
||||
lastRow.find('select').val('input');
|
||||
// then "reset" the form
|
||||
lastRow.find('input.label').val(defaultValue).addClass('void');
|
||||
|
||||
// warn the sortable widget about the new row
|
||||
$("fieldset.fields ol").sortable('refresh');
|
||||
@ -124,7 +123,7 @@ $(document).ready(function() {
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
var alias = link.parent().prevAll('.alias').val();
|
||||
var alias = link.parent().prevAll('.alias').val().trim();
|
||||
if (alias == '') alias = makeSlug(link.parent().prevAll('.label').val());
|
||||
$('#fancybox-wrap #custom_fields_field__alias').val(alias);
|
||||
|
||||
@ -132,8 +131,10 @@ $(document).ready(function() {
|
||||
$('#fancybox-wrap #custom_fields_field_hint').val(hint);
|
||||
},
|
||||
onCleanup: function() {
|
||||
link.parent().prevAll('.alias').val($('#fancybox-wrap #custom_fields_field__alias').val());
|
||||
link.parent().prevAll('.hint').val($('#fancybox-wrap #custom_fields_field_hint').val());
|
||||
var alias = $('#fancybox-wrap #custom_fields_field__alias').val().trim();
|
||||
if (alias != '') link.parent().prevAll('.alias').val(alias);
|
||||
var hint = $('#fancybox-wrap #custom_fields_field_hint').val().trim();
|
||||
if (hint != '') link.parent().prevAll('.hint').val(hint);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
@ -1,4 +1,5 @@
|
||||
function makeSlug(val, sep) { // code largely inspired by http://www.thewebsitetailor.com/jquery-slug-plugin/
|
||||
if (typeof val == 'undefined') return('');
|
||||
if (typeof sep == 'undefined') sep = '_';
|
||||
var alphaNumRegexp = new RegExp('[^a-zA-Z0-9\\' + sep + ']', 'g');
|
||||
var avoidDuplicateRegexp = new RegExp('[\\' + sep + ']{2,}', 'g');
|
||||
@ -7,3 +8,7 @@ function makeSlug(val, sep) { // code largely inspired by http://www.thewebsitet
|
||||
val = val.replace(avoidDuplicateRegexp, sep);
|
||||
return val.toLowerCase();
|
||||
}
|
||||
|
||||
String.prototype.trim = function() {
|
||||
return this.replace(/^\s+/g, '').replace(/\s+$/g, '');
|
||||
}
|
149
spec/lib/locomotive/heroku_spec.rb
Normal file
149
spec/lib/locomotive/heroku_spec.rb
Normal file
@ -0,0 +1,149 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe 'Heroku support' do
|
||||
|
||||
before(:each) do
|
||||
::Heroku::Client.any_instance.stubs(:post).returns(true)
|
||||
::Heroku::Client.any_instance.stubs(:delete).returns(true)
|
||||
end
|
||||
|
||||
context '#loaded' do
|
||||
|
||||
it 'has method to enable heroku' do
|
||||
Locomotive.respond_to?(:enable_heroku).should be_true
|
||||
end
|
||||
|
||||
it 'tells heroku is disabled' do
|
||||
Locomotive.heroku?.should be_false
|
||||
end
|
||||
|
||||
it 'does not add instance methods to Site' do
|
||||
Site.instance_methods.include?(:add_heroku_domains).should be_false
|
||||
Site.instance_methods.include?(:remove_heroku_domains).should be_false
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
context '#disabled' do
|
||||
|
||||
before(:each) do
|
||||
Locomotive.configure do |config|
|
||||
config.heroku = false
|
||||
end
|
||||
end
|
||||
|
||||
it 'has a nil connection' do
|
||||
Locomotive.heroku_connection.should be_nil
|
||||
end
|
||||
|
||||
it 'tells heroku is disabled' do
|
||||
Locomotive.heroku?.should be_false
|
||||
end
|
||||
|
||||
it 'does not add methods to Site' do
|
||||
Site.instance_methods.include?(:add_heroku_domains).should be_false
|
||||
Site.instance_methods.include?(:remove_heroku_domains).should be_false
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
context '#enabled' do
|
||||
|
||||
it 'tells heroku is disabled' do
|
||||
configure_locomotive_with_heroku
|
||||
Locomotive.heroku?.should be_true
|
||||
end
|
||||
|
||||
it 'raises an exception if no app name is given' do
|
||||
lambda {
|
||||
configure_locomotive_with_heroku(:name => nil)
|
||||
}.should raise_error
|
||||
end
|
||||
|
||||
context 'dealing with heroku connection' do
|
||||
|
||||
it 'opens a heroku connection with provided credentials' do
|
||||
configure_locomotive_with_heroku
|
||||
Locomotive.heroku_connection.user.should == 'john@doe.net'
|
||||
Locomotive.heroku_connection.password.should == 'easyone'
|
||||
end
|
||||
|
||||
it 'opens a heroku connection with env credentials' do
|
||||
ENV['HEROKU_LOGIN'] = 'john@doe.net'; ENV['HEROKU_PASSWORD'] = 'easyone'
|
||||
Locomotive.configure { |config| config.heroku = true }
|
||||
Locomotive.heroku_connection.user.should == 'john@doe.net'
|
||||
Locomotive.heroku_connection.password.should == 'easyone'
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
context 'enhancing site' do
|
||||
|
||||
before(:each) do
|
||||
configure_locomotive_with_heroku
|
||||
(@site = Factory.build(:site)).stubs(:valid?).returns(true)
|
||||
end
|
||||
|
||||
it 'calls add_heroku_domains after saving a site' do
|
||||
@site.expects(:add_heroku_domains)
|
||||
@site.save
|
||||
end
|
||||
|
||||
it 'calls remove_heroku_domains after saving a site' do
|
||||
@site.expects(:remove_heroku_domains)
|
||||
@site.destroy
|
||||
end
|
||||
|
||||
context 'adding domain' do
|
||||
|
||||
it 'does not add new domain if no delta' do
|
||||
Locomotive.heroku_connection.expects(:add_domain).never
|
||||
@site.save
|
||||
end
|
||||
|
||||
it 'adds a new domain if new one' do
|
||||
@site.domains = ['www.acme.fr']
|
||||
Locomotive.heroku_connection.expects(:add_domain).with('locomotive', 'www.acme.fr')
|
||||
@site.save
|
||||
Locomotive.heroku_domains.should include('www.acme.fr')
|
||||
end
|
||||
end
|
||||
|
||||
context 'removing domain' do
|
||||
|
||||
it 'does not remove domain if no delta' do
|
||||
Locomotive.heroku_connection.expects(:remove_domain).never
|
||||
@site.destroy
|
||||
end
|
||||
|
||||
it 'removes domains if we destroy a site' do
|
||||
@site.stubs(:domains_without_subdomain).returns(['www.acme.com'])
|
||||
Locomotive.heroku_connection.expects(:remove_domain).with('locomotive', 'www.acme.com')
|
||||
@site.destroy
|
||||
Locomotive.heroku_domains.should_not include('www.acme.com')
|
||||
end
|
||||
|
||||
it 'removes domain if removed' do
|
||||
@site.domains = ['www.acme.fr']; @site.save
|
||||
@site.domains = ['www.acme.com']
|
||||
Locomotive.heroku_connection.expects(:remove_domain).with('locomotive', 'www.acme.fr')
|
||||
@site.save
|
||||
Locomotive.heroku_domains.should_not include('www.acme.fr')
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def configure_locomotive_with_heroku(options = {}, domains = nil)
|
||||
::Heroku::Client.any_instance.stubs(:list_domains).with('locomotive').returns(domains || [
|
||||
{ :domain => "www.acme.com" }, { :domain => "example.com" }, { :domain => "www.example.com" }
|
||||
])
|
||||
Locomotive.configure do |config|
|
||||
config.heroku = { :name => 'locomotive', :login => 'john@doe.net', :password => 'easyone' }.merge(options)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
@ -2,6 +2,10 @@ require 'spec_helper'
|
||||
|
||||
describe Site do
|
||||
|
||||
before(:each) do
|
||||
Locomotive.stubs(:add_heroku_domain).returns(true)
|
||||
end
|
||||
|
||||
it 'should have a valid factory' do
|
||||
Factory.build(:site).should be_valid
|
||||
end
|
||||
|
@ -12,6 +12,7 @@ Rspec.configure do |config|
|
||||
config.mock_with :mocha
|
||||
|
||||
config.before(:each) do
|
||||
Locomotive.config.heroku = false
|
||||
Mongoid.master.collections.select { |c| c.name != 'system.indexes' }.each(&:drop)
|
||||
end
|
||||
end
|
||||
|
@ -1,5 +1,6 @@
|
||||
Locomotive.configure do |config|
|
||||
config.default_domain = 'example.com'
|
||||
config.enable_logs = true
|
||||
end
|
||||
|
||||
module Locomotive
|
||||
|
Loading…
Reference in New Issue
Block a user