SQLRunner
SQLRunner allows you to load your queries out of SQL files, without using ORMs. Available only for PostgreSQL.
Installation
Add this line to your application's Gemfile:
gem "sql_runner"
And then execute:
$ bundle
Or install it yourself as:
$ gem install sql_runner
Usage
Run raw queries:
SQLRunner.connect("postgresql:///test?application_name=myapp")
SQLRunner.execute("SELECT 1")
#=> <PG:Result:0x007fdf8d3475f0>
SQLRunner.execute("SELECT 1").values
#=> [["1"]]
SQLRunner.execute("SELECT :number::integer", number: 1).values
#=> [["1"]]
Run SQL files:
SQLRunner.root_dir = "#{__dir__}/sql"
class GetMembers < SQLRunner::Query
# by default will use root_dir/get_members.sql
end
Specify custom SQL:
class GetMembers < SQLRunner::Query
query "SELECT * FROM members ORDER BY created_at"
end
Specify custom connection:
class GetMembers < SQLRunner::Query
connect "postgresql:///another_database"
end
Specify other options:
class GetMembers < SQLRunner::Query
query_name "users" #=> will load root_dir/users.sql
root_dir "/some/path"
end
Plugins
Load just one record
class FindUserByEmail < SQLRunner::Query
plugin :one
query "SELECT * FROM users WHERE email = :email LIMIT 1"
end
FindUserByEmail.call(email: "[email protected]")
#=> {"id" => 1, "email" => "[email protected]"}
FindUserByEmail.call(email: "invalid")
#=> nil
FindUserByEmail.call!(email: "invalid")
#=> raise exception SQLRunner::RecordNotFound
Use a model
require "virtus"
class UserModel
include Virtus.model
attribute :id, String
attribute :email, String
attribute :name, String
end
class FindUserByEmail < SQLRunner::Query
plugin :one
plugin model: UserModel
query "SELECT * FROM users WHERE email = :email LIMIT 1"
end
FindUserByEmail.call(email: "[email protected]")
#=> <UserModel:0x007fdf8c2c1280>
Avoid calling .to_a in collections
class FindUsers < SQLRunner::Query
plugin :many
query "SELECT * FROM users"
end
FindUsers.call
#=> [{"id" => "1", "email" => "[email protected]"}]
Adding new plugins
First you have to create a class/module that implements the .activate(target, options) class method. The following example overrides the call(**bind_vars) method by using Module.prepend.
module ReverseRecords
def self.activate(target, )
target.singleton_class.prepend self
end
def call(**bind_vars)
super(**bind_vars).to_a.reverse
end
end
# Register the plugin.
SQLRunner::Query.register_plugin :reverse, ReverseRecords
class Users < SQLRunner::Query
query "SELECT * FROM users ORDER BY created_at ASC"
plugin :reverse
end
Users.call
If you plugin can receive options, you can call it as plugin reverse: options, where options can be anything (e.g. Hash, Array, Object, etc).
Benchmarks
You won't gain too much performance by using this gem. These are the results against ActiveRecord using different wrapping libraries like virtus and dry-types.
Loading just one record:
sql_runner - find one (raw) : 5518.6 i/s
sql_runner - find one (dry-types): 5015.4 i/s - same-ish: difference falls within error
sql_runner - find one (virtus) : 4746.2 i/s - 1.16x slower
activerecord - find one : 3468.5 i/s - 1.59x slower
Loading several records:
sql_runner - find many (raw) : 6808.2 i/s
sql_runner - find many (dry-types): 5251.4 i/s - same-ish: difference falls within error
sql_runner - find many (virtus) : 4145.6 i/s - 1.64x slower
activerecord - find many : 2731.5 i/s - 2.49x slower
Development
After checking out the repo, run bin/setup to install dependencies. Then, run rake test to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and tags, and push the .gem file to rubygems.org.
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/fnando/sql_runner. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.
License
The gem is available as open source under the terms of the MIT License.
