ENV variables not being read sinatra - heroku

So i am trying to serve my static assets from Amazon s3 locally and for Heroku, I was loading a yml file but that doesn't work as Heroku doesn't accept symlinks.
So i was given the suggestion to use ENV variables as Heroku also uses these. I have a rake task to precompile the assets to AWS. two problems at the moment
1) My ENV variables are not being read.(Fog directory can't be blank, Aws access key can't be blank, Aws secret access key can't be blank
2) When running a rake task i also get the error 'already initialized constant VALID_CHARACTER'
So the activesupport constant is being loaded twice?
My setup
env.rb
ENV['aws_bucket'] = 'bucketname'
ENV['aws_access_key'] = 'myaccesskey'
ENV['aws_secret_key'] = 'mysecretkey'
Rakefile
require 'bundler/setup'
Bundler.require(:default)
require './env' if File.exists?('env.rb')
AssetSync.configure do |con|
con.fog_provider = 'AWS'
con.fog_region = 'eu-west-1'
con.fog_directory = ENV['aws_bucket']
con.aws_access_key_id = ENV['aws_access_key']
con.aws_secret_access_key = ENV['aws_secret_key']
con.prefix = "assets"
con.public_path = Pathname("./public")
end
namespace :assets do
desc "Precompile assets"
task :precompile do
AssetSync.sync
end
Gemfile
source :rubygems
gem 'sinatra'
gem 'pony'
gem 'sinatra-flash'
gem 'heroku'
gem 'asset_sync', git: 'git://github.com/ejholmes/asset_sync.git', branch: 'sinatra'
UPDATE
AssetSync has activesupport in it's gemspec so that's getting included any way. It seems to be conflicting with the constant defined in the mail gem from the pony gemspec.
So with the Pony gem removed i can precompile assets locally, but when i try and compile for heroku nothing happens, it starts the rake task but then just goes back to the terminal ready for a new command.
The other thing is i need Pony for my mailer, how can i get around this?

To get rid of the clash between Pony and when running Rake locally, put the gems into different groups, e.g.
# Gemfile
group :assets do
gem 'asset_sync', git: 'git://github.com/ejholmes/asset_sync.git', branch: 'sinatra'
end
group :mail do
gem "pony"
end
# moreā€¦
in the Rakefile
Bundler.require(:assets,:database,:whatever_else_you_need)
in the rackup/app file
Bundler.require(default,:assets,:database,:mail,:whatever_else_you_need)
As to your other problem, you should set the env vars for production via heroku config (see https://devcenter.heroku.com/articles/config-vars) and load them locally using the Rakefile as I said in the other question you asked about this. The env vars will live for the extent of the Ruby process, so if you load them in via Rake and also start the local server within the same Rake process you'll get Sinatra picking up all the env vars.
Edit: env vars will last as long as the process that added them, so if you put them in a dependent task the following task will have access to them:
namespace :assets do
task :environment do
AssetSync.configure do |con|
con.fog_provider = 'AWS'
con.fog_region = 'eu-west-1'
con.fog_directory = ENV['aws_bucket']
con.aws_access_key_id = ENV['aws_access_key']
con.aws_secret_access_key = ENV['aws_secret_key']
con.prefix = "assets"
con.public_path = Pathname("./public")
end
end
desc "Precompile assets"
task :precompile => :"assets:environment" do
AssetSync.sync
end

You might want to split this into different questions. This makes it easier to help you. As for your first question: I assume you didn't put the env.rb under version control?
Why are your env vars not being picked up by Sinatra? Because you configure Fog in the Rakefile, and Simatra never sees that file. It's used by rake only.
I'd suggest you put the Fog configuration into a third file and require that in both Rakefile and Sinatra app.

Related

Ruby Gem: Install configuration files to the user-home directory

I have to make a REST-Client in Ruby.
The client must be runnable from the command-line like a binary and also it must be "requirable" in a ruby script and provide different functions.
My gemspec does exactly what it should.
But i have no idea how to install a configuration file (YAML) in the user-home folder?
The config file should be in the user directory to provide easy access for the user.
Is this even possible?
Should i check on the first run if there is a config file and create it?
Can i execute an own installation routine while installing a gem?
I did exactly the same thing in Python and it worked fine, so the Ruby client should behave similar.
For such decisions, I wrote gem persey. If you look at the description of the use of this gem, you can see that it provides what you expect:
# Rails.root are not initialized here
app_path = File.expand_path('../../', __FILE__)
# ...
# config with secret keys
# you don't want store this config in repository and copy to secret folder on host machine
my_secret_key_config = '/home/user/secret/keys.yml'
# ...
# Persey.init ENV["environment"] do # set current environment
Persey.init Rails.env do # set current environment
source :yaml, my_secret_key_config, :secret # no comments. It's secret!
env :production do
# ...
end
env :development, :parent => :production do
# ...
end
end

How can I load ActiveRecord database tasks on a Ruby project outside Rails?

ActiveRecord 3.2.14
I want to use ActiveRecord in a non-Rails Ruby project. I want to have available the rake tasks that are defined by ActiveRecord. How can I do that?
rake db:create # Create the database from DATABASE_URL or config/database.yml for the current Rails.env (use db:create:all to create all dbs in the config)
rake db:drop # Drops the database using DATABASE_URL or the current Rails.env (use db:drop:all to drop all databases)
rake db:fixtures:load # Load fixtures into the current environment's database
rake db:migrate # Migrate the database (options: VERSION=x, VERBOSE=false)
rake db:migrate:status # Display status of migrations
rake db:rollback # Rolls the schema back to the previous version (specify steps w/ STEP=n)
rake db:schema:dump # Create a db/schema.rb file that can be portably used against any DB supported by AR
rake db:schema:load # Load a schema.rb file into the database
rake db:seed # Load the seed data from db/seeds.rb
rake db:setup # Create the database, load the schema, and initialize with the seed data (use db:reset to also drop the db first)
rake db:structure:dump # Dump the database structure to db/structure.sql
rake db:version # Retrieves the current schema version number
The above list is the list of tasks that I want to be able to use on my non-Rails Ruby project that uses ActiveRecord. What do I have to write in my Rakefile?
Thanks in advance
The easiest thing to do is to load the tasks already defined in databases.rake. Here is a GIST of how it was done.
Inspired by this GIST by Drogus
Rakefile.rb
require 'yaml'
require 'logger'
require 'active_record'
include ActiveRecord::Tasks
class Seeder
def initialize(seed_file)
#seed_file = seed_file
end
def load_seed
raise "Seed file '#{#seed_file}' does not exist" unless File.file?(#seed_file)
load #seed_file
end
end
root = File.expand_path '..', __FILE__
DatabaseTasks.env = ENV['ENV'] || 'development'
DatabaseTasks.database_configuration = YAML.load(File.read(File.join(root, 'config/database.yml')))
DatabaseTasks.db_dir = File.join root, 'db'
DatabaseTasks.fixtures_path = File.join root, 'test/fixtures'
DatabaseTasks.migrations_paths = [File.join(root, 'db/migrate')]
DatabaseTasks.seed_loader = Seeder.new File.join root, 'db/seeds.rb'
DatabaseTasks.root = root
task :environment do
ActiveRecord::Base.configurations = DatabaseTasks.database_configuration
ActiveRecord::Base.establish_connection DatabaseTasks.env.to_sym
end
load 'active_record/railties/databases.rake'
You could try the standalone-migrations gem:
https://github.com/thuss/standalone-migrations
For Rails 3.x:
You need to manually create the tasks. As example here is how to add them (this example uses the environment variables like Rails):
namespace :db do
desc "Drop and create the current database"
task :recreate => :environment do
abcs = ActiveRecord::Base.configurations
ActiveRecord::Base.establish_connection(abcs[RAILS_ENV])
ActiveRecord::Base.connection.recreate_database(ActiveRecord::Base.connection.current_database)
end
end
and you'll have the task rake db:recreate available
For Rails 4.x:
If you want to have the ActiveRecord rake tasks available in your ruby app, take a look at the documentation.
Example usage of DatabaseTasks outside Rails could look as such:
include ActiveRecord::Tasks
DatabaseTasks.database_configuration = YAML.load(File.read('my_database_config.yml'))
DatabaseTasks.db_dir = 'db'
# other settings...
DatabaseTasks.create_current('production')
Also you have here an example on how to use ActiveRecord in your ruby aplication.
Create your own!
Reference the Rails one though:
https://github.com/rails/rails/blob/master/activerecord/lib/active_record/railties/databases.rake
Create a Rake Task file. To use Rake, generally you want a tasks folder filled with Rake task files. These files have the ".task" extension.
Study the file to link given.
Take parts of that file, or even the entire contents of the file, and add it to your new Rake task file.
Make sure your Rakefile loads those task files. Your Rakefile should have something like this
-
Dir[File.join(PROJECT_ROOT, 'tasks', '**', '*.rake')].each do |file|
load file
end
I believe you can use the sinatra-activerecord gem even if you're not using Sinatra. I just solved this problem by requiring that gem and then adding
require 'sinatra/activerecord/rake'
to my rakefile.
Once I added that require line the db tasks showed up in my rake -T!
If you are using Sinatra, you can use this gem:
https://github.com/janko-m/sinatra-activerecord
However, if you don't use it either, the source code inside provides a good example on how to implement AR rake tasks.

How to get the current Rack environment in Rake?

Is there a way to get information about the current Rack environment in Rake? For example, how can I tell whether Rack is running in development or production mode?
I understand that Rake is not Rack-aware. I'm trying to avoid replicating code in nearly-identical Rake tasks between production and dev environments.
Question is old but never fetched the best practice answer or a satisfying answer at all.
The real question is: How to go sure which environment is used in a Rake task in order to load the correct configuration / hitting into correct if-conditions.
Note: As Rake doesn't give much about Rack (Rake is not using HTTP) to
rely on the RACK_ENV is basically wrong but common and handy if a Rake
task loads your main Sinatra application (the RACK_ENV is required to let
Sinatras development? / test? / production? being set correctly).
The answer: Set the environment with each Rake task call.
Command line call:
/usr/bin/rake namespace:task_name RACK_ENV=production
Cronjob call (in crontab):
cd /into/your/app/root && /usr/bin/rake namespace:task_name RACK_ENV=production --silent
Note: To specify the path of the Rake bin is not necessary if you have it in your global system variables. Your path might differs from mine used in the examples, check on Unix systems with: whereis rake
You can check the RACK_ENV in your tasks via:
puts ENV["RACK_ENV"]
As other environment variable, you can retrieve it using:
ENV['RACK_ENV']
Considering it's a Sinatra application, and that you've set the environment into config/environment.rb, you can add the following to your Rakefile:
task :environment do
require File.expand_path('config/environment', File.dirname(__FILE__))
end
task :your_task => :environment do
# task
end
Then, you can retrieve the environment (depending how you set it up in your environment.rb) with ENV['RACK_ENV'] or Sinatra::Application.environment.
Considering there isn't a config/environment.rb config file, only the application file, for instance hello_world.rb, the following works:
hello_world.rb:
require 'sinatra'
set :environment, :production
get '/' do
'Hello World'
end
Rakefile:
task :environment do
require File.expand_path('hello_world', File.dirname(__FILE__)) # your Sinatra app
end
task :your_task => :environment do
puts Sinatra::Application.environment
end
When doing rake your_task you should obtain:
> rake your_task
production
After 2.5 years, I want to share what I've found to be the best solution.
Create a .env file in the root folder of the application, and add a flag specifying the application environment:
ENVIRONMENT=development
Then use Brandon Keepers' dotenv gem to load all environment variables from this file. Now you can use any environment variables specified in .env within Rake tasks.
Rake will rely on the explicit value set in .env, so you must create separate .env files for each environment you plan on using (e.g. dev, test, staging, production, etc).
Sample Rakefile:
require 'dotenv/tasks'
task :default => :help
desc 'Show this help menu'
task :help do
puts "Available rake tasks:"
system('rake --tasks')
end
# Will run in any environment
desc 'Demo task'
task :demo_task => :dotenv do
puts "Running demo task in '#{ENV['ENVIRONMENT']}' mode"
end
# Will only run if ENVIRONMENT value in .env file is set to 'production'
desc 'Production-only task'
task :production_task => :dotenv do
if ENV['ENVIRONMENT'] == 'production'
puts "Running 'Production-only' task"
else
puts "Won't run, because the environment is not set to PRODUCTION!"
end
end
# Will only run if ENVIRONMENT value in .env file is set to 'development'
desc 'Development-only task'
task :dev_task => :dotenv do
if ENV['ENVIRONMENT'] == 'development'
puts "Running 'Development-only' task"
else
puts "Won't run, because the environment is not set to DEVELOPMENT!"
end
end
If you want to use the environment variables within your Rack or Sinatra app (which you probably do), add the following to the application's config or bootstrap block:
require 'dotenv'
Dotenv.load

Environment variables locally and Heroku

I have a sinatra app in which i have a yml file to set environment variables, i call them using this method
module MyConfig
def config
environment = ENV["RACK_ENV"] || "development"
YAML.load_file("./config/config.yml")[environment]
end
end
so when i want to use a variable i do this for example
aws_access_key_id = config['aws_access_key']
I have a .gitignore file that ignores config.yml when pushing to github for example.So when I push to heroku these environment variables will not be accessible?
So this leaves me with using the heroku way of setting them like so
heroku config:add aws_access_key= myapikey
but heroku accesses these like
aws_access_key_id = ENV['aws_access_key']
How can i set my dev environment to use method config and heroku use ENV, am i looking at this the wrong way? or does my config method do this for me?
Any help appreciated
RAKEFILE
require 'active_support/core_ext'
require './config/config.rb'
require 'bundler/setup'
Bundler.require(:default)
include MyConfig
AssetSync.configure do |con|
con.fog_provider = 'AWS'
con.fog_region = 'eu-west-1'
con.fog_directory = config['fog_directory']
con.aws_access_key_id = config['aws_access_key']
con.aws_secret_access_key = config['aws_secret_key']
con.prefix = "assets"
con.public_path = Pathname("./public")
end
namespace :assets do
desc "Precompile assets"
task :precompile do
AssetSync.sync
end
end
Update:
I now use the dotenv gem instead of the example below. So instead of ignoring the env.rb file, I now ignore the .env file with Git.
Original post:
Try this,
# /env.rb
ENV['aws_bucket'] = 'my_bucket'
ENV['aws_access_key'] = 'my_access_key'
ENV['aws_access_secret'] = 'my_access_secret'
This file sets the same ENV values as heroku config would do.
# /config.rb
require './env' if File.exists?('env.rb')
The env.rb will only get required if it exists.
# /.gitignore
/env.rb
The env.rb has been added to the .gitignore file so it isn't kept in Git.
You would then access the values using ENV['key'] instead of config['key'].
You might need to change the path to the env.rb if it's not in the same directory as the config.rb file.
EDIT:
From looking at your Rakefile in the previous question, you need to change it to this:
# Rakefile
require 'bundler/setup'
Bundler.require(:default)
require './env' if File.exists?('env.rb')
AssetSync.configure do |con|
con.fog_provider = 'AWS'
con.fog_region = 'eu-west-1'
con.fog_directory = ENV['aws_bucket']
con.aws_access_key_id = ENV['aws_access_key']
con.aws_secret_access_key = ENV['aws_access_secret']
con.prefix = "assets"
con.public_path = Pathname("./public")
end
namespace :assets do
desc "Precompile assets"
task :precompile do
AssetSync.sync
end
end
I've assumed that the only method in /config/config.rb was the config method so I've removed the,
require './config/config.rb'
include MyConfig
And swapped the config[key] for the ENV[key] values defined in env.rb. You may need to change the key names to match up.
You could delete the yaml, and describe the environment variables in a .env file then start your app with foreman start. See https://devcenter.heroku.com/articles/config-vars#local-setup
Or keep your hybrid system, where you load a yaml in dev, and use environment variables on heroku.
I do something similar to Sam's suggestion, but a little bit different. I have a YAML config file too, but I wrap the reading of it in a Rake task, which then runs the app.
# in the Rakefile
require 'yaml'
def set_connstring
s = %Q!postgres://#{ENV["DB_APP"]}#localhost/#{ENV["DB_APP"]}!
ENV['DATABASE_URL'] ||= ENV["RACK_ENV"] == "test" ? "#{s}.test" : s
end
def basic_environment
warn " Setting up environment..."
file = File.expand_path( File.join File.dirname(__FILE__), "./config.yml" )
if File.exist? file
YAML.load_file(file).each do |k,v|
warn "-> #{k}"
ENV[k.upcase] = v
end
end
set_connstring()
end
namespace :app do
desc "Set up the environment locally"
task :environment do
basic_environment()
end
desc "Run the app locally"
task :run_local => "app:environment" do
exec "bin/rackup config.ru -p #{ENV['RUN_LOCAL_PORT']}"
end
end
It means I can run it locally without any code inside the app to deal with this.
Edit: a quick aside, I notice you have Bundler.require(:default) in your Rakefile. If you use bundle install --binstubs then Bundler installs all executables into a dir named "bin/" within the project. Then, if you run any of those executables they automatically use the libraries installed by Bundler, no need to require via Bundler. See http://gembundler.com/v1.2/man/bundle-exec.1.html.
Exporting directly from heroku admin:
Settings -> Reveal Config Vars
Then open browser js console, paste this and type enter...
k=[];
$(".config-var-list input").map(function(y, x){k.push($(x).val())});
v=[];
$(".config-var-list textarea").map(function(y, x){v.push($(x).val())});
ret="";
k.map(function(x, i){ret+=k[i]+"\t"+v[2*i]+"\n"});
console.info(ret);

Gem testing with Rspec

I have written a custom Ruby Gem to hook into our company's authentication and authorization system and am starting to develop the unit tests for the gem.
In our rails app, the Gem can be configured via environment.rb and a custom initializer and yaml file containing the configuration values.
I need to translate the configuration of the Gem in rails to test the standalone Gem. How do I translate this over to Rspec to perform integration testing??
Gem configuration in rails
# environment.rb
MyGem.configure do |config|
config.url = MY_CONFIG ['url']
config.application_name = MY_CONFIG ['app_name']
config.application_id = MY_CONFIG ['app_id']
config.logger = Rails.logger
config.log_level = :debug
# Rails config/initalizers/load_config.rb
# Custom config file loading automatically done via initializers
MY_CONFIG = YAML.load_file("#{Rails.root.to_s}/config/my_config.yml")[Rails.env]
# config/my_config.yml
defaults: &defaults
url: http://url/to/service
app_name: my app
app_id: 1
development:
<<: *defaults
test:
<<: *defaults
production:
<<: *defaults
end
Here's a simple project where you can see how you'd go by doing it: multiplier
First and foremost, if you're doing the gem management by yourself, please don't, use helper tools like jeweler to do it for you. Install the jeweler gem (gem install jeweler) and once you have it installed, create your gem projet:
jeweler --rspec your_gem_name
With this, it's going to setup a skeleton gem that's going to have a single main file (where you would require your necessary gem files) and the spec folder.
At the spec folder there's spec_helper.rb, that's where our configuration lives, what I did was:
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
$LOAD_PATH.unshift(File.dirname(__FILE__))
require 'rspec'
require 'multiplier'
# Requires supporting files with custom matchers and macros, etc,
# in ./support/ and its subdirectories.
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
RSpec.configure do |config|
end
Multiplier.configure do |config| #these are the only lines I added myself
config.multiplier 4
end
So, here lives the config for our gem, but you could even do it on each spec, if you'd need it. But if you want to use a single config for all specs this is where you should place it.

Resources