How to suppress warning on PaperTrail gem with Sinatra app? - ruby

DEPRECATION WARNING: PaperTrail.track_associations has not been set. As of PaperTrail 5, it defaults to false. Tracking associations is an experimental feature so we recommend setting PaperTrail.config.track_associations = false in your config/initializers/paper_trail.rb . (called from require at /Users/george/.rbenv/versions/2.3.1/lib/ruby/2.3.0/rubygems/core_ext/kernel_require.rb:68)
Run options:
Since this is not a Rails app, there is no config/initializers/paper_trail.rb. Looked at https://github.com/airblade/paper_trail/blob/master/lib/generators/paper_trail/install_generator.rb, but didn't see a generator for the config file.
If I do
require 'paper_trail'
PaperTrail.config.track_associations = false
it still emits the warning.
Also tried:
def PaperTrail; end
PaperTrail.config.track_associations = false
require 'paper_trail'
This is a "Classic" Sinatra app.

The reason why this is happening is due the the call to require 'paper_trail' initializing a PaperTrail.config object: this line results in this file getting required, a part of which gets executed resulting in initializing the config singleton. We can solve this by first setting up the config object and then requiring the top-level paper_trail file later.
# app.rb
require 'sinatra'
require 'paper_trail/config'
config = PaperTrail::Config.instance
config.track_associations = true
# require models here
Dir["#{Dir.pwd}/models/*.rb"].each { |file| require file }
# rest of app code
And the models won't need any change:
# models/article.rb
require 'paper_trail'
class Article < ActiveRecord::Base
has_paper_trail
end
Thus, when the require 'paper_trail' call gets executed, we already have the correct configuration setup and the warning won't be displayed.

I added PaperTrail.config.track_associations = false to config/application.rb in the definition of class Application.

Related

annotate gem not working after including custom module inside model

After i include custom module inside model, annotate stop working and give me error:
My model: app/models/hotel.rb
class Hotel < ActiveRecord::Base
include HotelHandler
...
end
Custom helper class: app/helpers/hotel_handler.rb
module HotelHandler
...
end
This give me error:
uninitialized constant Hotel::HotelHandler (NameError)
If you want to setup Rails style auto and eager loading in a plain old Ruby project the modern way is to use Zeitwerk:
# this file would be the "entry path" to your application such as config.ru
# loads an installs gems inline
require 'bundler/inline'
gemfile do
source 'https://rubygems.org'
gem 'zeitwerk'
end
root = Pathname.new(File.expand_path(__dir__))
env = ENV.fetch("MYAPP_ENV", 'development')
loader = Zeitwerk::Loader.new
# Mimics Rails by adding every subdirectory of `/app` and `/app/**/concerns`
# as "root directories" where zeitwerk will find top level constants
paths = [
dir[root.join('app/*/')]
dir[root.join('app/**/concerns')]
].flatten.each do |path|
loader.push_dir(path) if File.directory?(path)
end
# makes zeitwerk reload classes when they change
loader.enable_reloading if env == 'development'
loader.setup
# loads all classes on boot for performance
loader.eager_load if env == 'production'
This is really just a quick and dirty minimal example and you would usually enscapsulate this logic into a bootstapping file and an "Application" class.

Using a rackup file to custom configure an application instance

tl;dr How can I get a single Sinatra app to start up very differently on different servers via customizations to config.ru?
Background
I have a single web application written using Sinatra that's run on different servers. Currently the codebase for these servers is forked because there are some non-trivial differences in the way (discrete) parts of them work. For example:
one server authenticates users via an intranet LDAP server, while another server uses a simpler local database table lookup.
one server uses an external cron job to periodically update some statistics, while another (Windows-based) server uses an internal sleepy Thread.
one server stores certain metadata in a local table, while another server pulls the metadata from an external Wiki via screen scraping (!).
…and so on.
I'd like to get these code bases completely shared (single Git repo). I envision that each server would have one slightly-differing configuration file that causes the app to be started up differently.
Abandoned Solutions
I could change the behavior of the app based on environment variables. As there are a not-tiny number of variations in behavior, I'd rather not hide the settings in environment variables.
I could create my own "server-settings.rb" file that is unique to each machine, require it in my app.rb, and then change the configuration there. However, this seems to possibly be re-inventing the wheel. I already have a file named config.ru for each server. Shouldn't I be using this?
The Current Code
My config.ru for the app currently is simply:
require ::File.join( ::File.dirname(__FILE__), 'app' )
run MyApp.new
And the app.rb that it requires is, in essence:
require 'sinatra'
require_relative 'helpers/login' # customized for LDAP lookup on this server
class MyApp < Sinatra::Application
use Rack::Session::Cookie, key:'foo.bar', path:'/', secret:'ohnoes'
set :protection, except: [:path_traversal, :session_hijacking]
configure :production do
# run various code that depends on server settings, e.g.
Snapshotter.start # there is no cron on this machine, so we do it ourselves
end
configure :development do
# run various code that depends on server settings
end
end
The Question
I'd like to make config.ru live up to its name, and have it look something like this:
require ::File.join( ::File.dirname(__FILE__), 'app' )
run MyApp.new( auth: :ldap, snapshot:false, metadata: :remote_wiki, … )
How can I modify my application to change its configuration behavior based on settings supplied via config.ru? Or is this an abuse of config.ru, trying to use it for totally the wrong thing?
As soon as I started reading the question the first answer to pop into my head was "environment variable" but you scotched that straight away :)
I'll go with a mixture of one of your coulds and the desired outcome code, as it's how I structure things…
Because I want to be able to test my applications more easily, I take most of the Ruby out of the config.ru and into a separate config.rb file and leave config.ru to be a bootstrap file. So my standard skel is:
config.ru
# encoding: UTF-8
require 'rubygems'
require 'bundler'
Bundler.setup
root = File.expand_path File.dirname(__FILE__)
require File.join( root , "./app/config.rb" )
# everything was moved into a separate module/file to make it easier to set up tests
map "/" do
run APP_NAME.app
end
app/config.rb
# encoding: utf-8
require_relative File.expand_path(File.join File.dirname(__FILE__), "../lib/ext/warn.rb")
require_relative "./init.rb" # config
require_relative "./main.rb" # routes and helpers
require 'encrypted_cookie'
# standard cookie settings
COOKIE_SETTINGS = {
:key => 'usr',
:path => "/",
:expire_after => 86400, # In seconds, 1 day
:secret => ENV["LLAVE"],
:httponly => true
}
module APP_NAME # overall name of the app
require 'rack/ssl' # force SSL
require 'rack/csrf'
if ENV["RACK_ENV"] == "development"
require 'pry'
require 'pry-nav'
end
# from http://devcenter.heroku.com/articles/ruby#logging
$stdout.sync = true
ONE_MONTH = 60 * 60 * 24 * 30
def self.app
Rack::Builder.app do
cookie_settings = COOKIE_SETTINGS
# more security if in production
cookie_settings.merge!( :secure => true ) if ENV["RACK_ENV"] == "production"
# AES encryption of cookies
use Rack::Session::EncryptedCookie, cookie_settings
if ENV["RACK_ENV"] == "production"
use Rack::SSL, :hsts => {:expires => ONE_MONTH}
end
# to stop XSS
use Rack::Csrf, :raise => true unless ENV["RACK_ENV"] == "test"
run App # the main Sinatra app
end
end # self.app
end # APP_NAME
The initial reason I did this was making it easy to run the app in specs:
shared_context "All routes" do
include Rack::Test::Methods
let(:app){ APP_NAME.app }
end
but it makes sense to me to keep this code with the rest of the application code, so to speak, as I can bundle things together, run other apps etc. I've used this to conditionally load different examples into the specs in a few projects (it helps cut down on duplicated effort and check the examples really work), so I don't see why you couldn't use it to conditionally load configurations.
This way you get to choose to use a conditional in the config.ru as to which config.rb file you would use, or use an env var in the config.rb as to which definiton of self.app to use , or pass in an options hash to self.app…
With your set up I'd rename the APP_NAME module to MyApp, and the Sinatra class to App (because quite often I'll have an website that runs a front end and an API, so the Sinatra classes get named by their function (App, API etc) and wrapped in a module named after the site) and end up with:
config.ru
map "/" do
run MyApp.app( auth: :ldap, snapshot:false, metadata: :remote_wiki )
end
config.rb
def self.app( opts={} )
opts = DEFAULT_OPTIONS.merge opts
# …
run App
end
It'll be interesting to see how other people tackle this.

undefined method `configure' for Savon:Module

I'm getting the above error in a gem with this code snippet
Savon.configure do |config|
config.log = false
config.log_level = :error
HTTPI.log = false
end
This code used to pass in past runs on Travis, so I'm not sure why this changed when I altered the Readme.
Part of this confusion comes from my situation--inheriting a gem to maintain--along with this line in the gemspec:
gem.add_dependency 'savon'
There's no version number specified, so the newest run switched over to using Savon 2, which ditched the Savon.configure global behavior. If you're in the same boat as me, changing this line to the last pre-2.0 version of Savon will resolve the issue:
gem.add_dependency 'savon', '~>1.2.0'
Then bundle install and you should be good.
Or you want to upgrade your code. I know I do.
Savon.configure was removed from Savon 2.0 because the "problem was global state". The quickest way to keep the behavior the same in your app would be to define a app-level global hash in the same place. You'd then pass this hash into every Savon.client call you make. For instance:
# Where Savon.configure was called
APP_OPTS = {
# disable request logging, silences HTTPI as well
log: false,
# Don't log Laundry xmls to STDOUT
log_level: :error,
#... etc
}
# Elsewhere
#client = Savon::Client.new(APP_OPTS)
I'd consider this a starting point to migrating to the 2.0 configuration style. Ideally, you should always consider the client-specific 2.0 options available when initializing each Savon client.

Sinatra Helper in External File gives LoadError

I'm trying to add a helper to connect to a mongo db to my modular Sinatra application
When I type foreman start in my console I get:
/home/sunny/Programs/landing_pages/app.rb:17:in `block in <class:LandingPages>': undefined local variable or method `connect' for LandingPages:Class (NameError)
My app.rb file looks like this:
require 'sinatra/base'
require 'sinatra/partial'
require 'sinatra/db_helper'
require 'bundler/setup'
require 'mongo'
class LandingPages < Sinatra::Base
helpers Sinatra::DbHelper
configure do
$collection = connect
end
end
My ./lib/sinatra/db_helper.rb looks like this:
require 'sinatra/base'
module Sinatra
module DbHelper
def connect
conn = Mongo::Connection.new("localhost")
db = conn.db("leads")
db.collection("laws")
end
end
helpers DbHelper
end
My config.ru looks like this:
require './app'
run LandingPages
I thought I was following the instructions correctly on:
http://www.sinatrarb.com/extensions.html
but I'm not totally sure. I'm not making a gem but just a sinatra app so maybe my directory hierarchy isn't correct. I don't have a rake file or a gem spec. Do I need them?
Some googling also found this:
sinatra helper in external file
Dave Sag answers my question perfectly but I can't get it work.
This comes about because of the scope of methods created through the helpers is on the sinatra application instance, since it calls ruby's include under the hood. So this would work:
get '/some/route' do
db = connect
# do something else ...
end
But the configure block has a class scope, so it can be used for configuring the application as a whole. So to make this work, you can define the method as:
module Sinatra
module DbHelper
def self.connect
conn = Mongo::Connection.new("localhost")
db = conn.db("leads")
db.collection("laws")
end
end
end
which could then be called via: $collection = Sinatra::DbHelper.connect or perhaps more favoured, you could call register instead of helpers. register calls extend under the hood, so you end up with class level methods (if you extend a class, anyway). You could then make the configure block as so:
configure do |app|
$collection = app.connect
end
You could also do all of this in an registered method on the DbHelpers module. See the example in the documentation for how this might work.

Creating a class that wraps my config.yml file, and using it in my rake files

This is a non-rails app, just a simple ruby script that uses rake etc. to automate some things.
My folder layout is this:
/scripts/Rakefile
/scripts/config/config.yml
/scripts/tasks/*.rake (various rake files with namespaces to organize them)
/scripts/lib/settings.rb
Now I want to create a Settings class that will load the config yaml file, and then expose properties/methods for the contents of the yaml file.
The yaml file has separate sections for development and production.
development:
scripts_path: '/dev/mygit/app1/scripts/'
production:
scripts_path: '/var/lib/app1/scripts/'
My rakefile so far looks like:
$LOAD_PATH.unshift File.expand_path('..', __FILE__)
#imports
require 'fileutils'
require 'rubygems'
require 'active_record'
require 'yaml'
require 'logger'
require 'ar/models'
require 'lib/app1'
env = ENV['ENV'] || 'development'
config = YAML::load(File.open('config/config.yml'))[env]
Dir.glob('tasks/*.rake').each { |r| import r }
I need help with the Settings.rb file, is this right?
module App1
class Settings
def initialize(config_path, env)
config = YAML.load(File.open(config_path))
end
def scripts_path
end
end
end
How can I pass in the env, and then read the correct value from the config for each method like scripts_path etc?
Now suppose each *.rake file needs to reference my Settings.rb file somehow (to get the config related information). How should I do this? Since my settings needs the path of the config.yml file, do I have to do this in each rake file?
Update
Sorry, this isn't a Rails app, just some ruby scripts.
I would do it quite simple.
You don't need a complex solution.
require 'ostruct'
require 'yaml'
MY_ENV = ENV['ENV'] || 'development'
CONFIG = OpenStruct.new(YAML.load_file("config/config.yml")[MY_ENV])
Stick this at the top of your rakefile
and CONFIG will be available in all rake tasks.
Just call CONFIG.scripts_path
Inside my applications I do something of this sort.
# config/application.yml
development:
some_variable: a string
production:
some_variable: a different string
Then in application.rb I load it up.
# config/application.rb
module MyApp
def self.config
#config ||= OpenStruct.new(YAML.load_file("config/application.yml")[Rails.env.to_s])
end
class Application < Rails::Application
...
In this case, anywhere the environment is loaded I can say
MyApp.config.some_variable
To get access to this inside a rake task, I just need to include environment
task :something => :environment do
MyApp.config.some_variable
# do something with it
end

Resources