• Stars
    star
    390
  • Rank 106,161 (Top 3 %)
  • Language
    Ruby
  • License
    MIT License
  • Created about 10 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Useful tools to help construct database queries with ActiveRecord and Arel.

Unit Tests

arel-helpers

Useful tools to help construct database queries with ActiveRecord and Arel.

Installation

gem install arel-helpers

Usage

require 'arel-helpers'

ArelTable Helper

Usually you'd reference database columns in Arel via the #arel_table method on your ActiveRecord models. For example:

class Post < ActiveRecord::Base
  ...
end

Post.where(Post.arel_table[:id].eq(1))

Typing ".arel_table" over and over again can get pretty old and make constructing queries unnecessarily verbose. Try using the ArelTable helper to clean things up a bit:

class Post < ActiveRecord::Base
  include ArelHelpers::ArelTable
  ...
end

Post.where(Post[:id].eq(1))

JoinAssociation Helper

Using pure Arel is one of the only ways to do an outer join with ActiveRecord. For example, let's say we have these two models:

class Author < ActiveRecord::Base
  has_many :posts

  # attribute id
  # attribute username
end

class Post < ActiveRecord::Base
  belongs_to :author
  has_many :comments

  # attribute id
  # attribute author_id
  # attribute subject
end

class Comment < ActiveRecord::Base
  belongs_to :post
  belongs_to :author

  # attribute id
  # attribute post_id
  # attribute author_id
end

A join between posts and comments might look like this:

Post.joins(:comments)

ActiveRecord introspects the association between posts and comments and automatically chooses the right columns to use in the join conditions.

Things start to get messy however if you want to do an outer join instead of the default inner join. Your query might look like this:

Post.joins(
  Post.arel_table.join(Comment.arel_table, Arel::Nodes::OuterJoin)
    .on(Post[:id].eq(Comment[:post_id]))
    .join_sources
)

Such verbose. Much code. Very bloat. Wow. We've lost all the awesome association introspection that ActiveRecord would otherwise have given us. Enter ArelHelpers.join_association:

Post.joins(
  ArelHelpers.join_association(Post, :comments, Arel::Nodes::OuterJoin)
)

Easy peasy.

Note that pretty much anything you can pass to ActiveRecord's #join method you can also pass to #join_association's second argument. For example, you can pass a hash to indicate a set of nested associations:

Post.joins(
  ArelHelpers.join_association(Post, { comments: :author })
)

This might execute the following query:

SELECT "posts".*
FROM "posts"
INNER JOIN "comments" ON "comments"."post_id" = "posts"."id"
INNER JOIN "authors" ON "authors"."id" = "comments"."author_id"

#join_association also allows you to customize the join conditions via a block:

Post.joins(
  ArelHelpers.join_association(Post, :comments, Arel::Nodes::OuterJoin) do |assoc_name, join_conditions|
    join_conditions.and(Post[:author_id].eq(4))
  end
)

But wait, there's more! Include the ArelHelpers::JoinAssociation concern into your models to have access to the join_association method directly from the model's class:

include ArelHelpers::JoinAssociation

Post.joins(
  Post.join_association(:comments, Arel::Nodes::OuterJoin) do |assoc_name, join_conditions|
    join_conditions.and(Post[:author_id].eq(4))
  end
)

Query Builders

ArelHelpers also contains a very simple class that's designed to provide a light framework for constructing queries using the builder pattern. For example, let's write a class that encapsulates generating queries for blog posts:

class PostQueryBuilder < ArelHelpers::QueryBuilder
  def initialize(query = nil)
    # whatever you want your initial query to be
    super(query || post.unscoped)
  end

  def with_title_matching(title)
    reflect(
      query.where(post[:title].matches("%#{title}%"))
    )
  end

  def with_comments_by(usernames)
    reflect(
      query
        .joins(comments: :author)
        .where(author[:username].in(usernames))
    )
  end

  def since_yesterday
    reflect(
      query.where(post[:created_at].gteq(Date.yesterday))
    )
  end

  private

  def author
    Author
  end

  def post
    Post
  end
end

The #reflect method creates a new instance of PostQueryBuilder, copies the query into it and returns the new query builder instance. This allows you to chain your method calls:

PostQueryBuilder.new
  .with_comments_by(['camertron', 'catwithtail'])
  .with_title_matching("arel rocks")
  .since_yesterday

Conditional reflections

If you have parts of a query that should only be added under certain conditions you can return reflect(query) from your method. E.g:

  def with_comments_by(usernames)
    if usernames
      reflect(
        query.where(post[:title].matches("%#{title}%"))
      )
    else
      reflect(query)
    end
  end

This can become repetitive, and as an alternative you can choose to prepend not_nil to your method definition:

  class PostQueryBuilder < ArelHelpers::QueryBuilder
    not_nil def with_comments_by(usernames)
      reflect(query.where(post[:title].matches("%#{title}%"))) if usernames
    end
  end

Requirements

Requires ActiveRecord >= 3.1.0, < 8. Depends on SQLite for testing purposes.

Running Tests

bundle exec rspec

Authors

More Repositories

1

rux

A jsx-inspired way to render view components in Ruby.
Ruby
379
star
2

scuttle-server

Server behind scuttle.io, a SQL editor and Arel converter.
Ruby
145
star
3

scuttle-rb

A library for transforming raw SQL statements into ActiveRecord/Arel queries. Ruby wrapper and tests for scuttle-java.
Ruby
86
star
4

rux-rails

Rux view components on Rails.
Ruby
83
star
5

gelauto

Automatically annotate your code with Sorbet type definitions.
Ruby
52
star
6

cldr-segmentation.js

CLDR text segmentation for JavaScript
JavaScript
35
star
7

active_nutrition

An ActiveRecord-backed collection of models for storing and retrieving nutritional information from the USDA's Nutrient Database.
Ruby
31
star
8

utfstring

UTF-safe string operations in JavaScript.
TypeScript
23
star
9

onload

A preprocessor system for Ruby.
Ruby
21
star
10

cskit-strongs-rb

Strong's concordance resources for CSKit.
Ruby
19
star
11

turbo-sprockets-rails4

Speed up asset precompliation by compiling assets in parallel.
Ruby
18
star
12

json-write-stream

An easy, streaming way to generate JSON.
Ruby
17
star
13

SQLParser

ANTLR4-based SQL Parser extracted from Apache Tajo
Java
15
star
14

antlr4-native-rb

Create native Ruby extensions from (almost) any ANTLR4 grammar.
Ruby
13
star
15

mosaico-rails

The Mosaico email editor on Rails.
Ruby
12
star
16

garnet-js

An implementation of the YARV virtual machine in TypeScript.
Ruby
12
star
17

prebundler

Experimental. Speed up gem installation by prebuilding gems and storing them in S3.
Ruby
11
star
18

trie-file

Memory-efficient cached trie and trie storage.
Ruby
9
star
19

viewcat

A faster ActionView::OutputBuffer written in C.
C
9
star
20

esprima-rb

Ruby wrapper around the Esprima static code analyzer for JavaScript.
Ruby
8
star
21

net-smtp-proxy

Proxy support for Ruby's Net::SMTP.
Ruby
8
star
22

antlr-gemerator

Generate a complete Rubygem from (almost) any ANTLR4 grammar.
ANTLR
6
star
23

llama.rb

llama.cpp for Ruby
C++
6
star
24

tmx-parser

Parser for the Translation Memory eXchange (.tmx) file format.
Ruby
5
star
25

Pongo

A modular, multi-threaded web application deployment framework written in PHP.
PHP
4
star
26

grape-client-generator

Automatically generate clients for your Grape APIs.
Ruby
4
star
27

twitter-windows

A (eventually) full-fledged Twitter client built to have the same look and feel as Twitter for Mac.
C#
4
star
28

jvectormap-rails

jVectorMap for the Rails asset pipeline
Ruby
4
star
29

turbo-sprockets-rails5

Speed up asset precompliation by compiling assets in parallel.
Ruby
3
star
30

rails-middleware-extensions

Adds several additional operations useful for customizing your Rails middleware stack.
Ruby
3
star
31

simple-graph

A simple, no-frills graph implementation.
Ruby
3
star
32

generated-assets

Programmatically generate assets for the Rails asset pipeline.
Ruby
3
star
33

rux-vscode

Rux syntax highlighting for VSCode.
3
star
34

cldr-plurals

Tokenizes and parses CLDR plural rules and provides a mechanism for emitting them as source code
JavaScript
3
star
35

i18n-js-assets

Compile your Javascript translations with the asset pipeline instead of a rake task.
Ruby
3
star
36

myrb

Inline types for Ruby
Ruby
3
star
37

rux-bootstrap

A collection of Rux view components for building webpages with Bootstrap.
Ruby
3
star
38

ohm-stateful-model

Integrate state machines (from the state_machine gem) into your Ohm models.
Ruby
3
star
39

scuttle-java

A library for transforming raw SQL statements into ActiveRecord/Arel queries.
Java
3
star
40

storybuilder

Drag-and-drop editor for Primer view components.
Ruby
2
star
41

cldr-plurals-runtime-rb

Ruby runtime methods for CLDR plural rules (see camertron/cldr-plurals).
Ruby
2
star
42

avoidance

Manipulate ActiveRecord models and their associations naturally without persisting them to the database.
Ruby
2
star
43

xml-write-stream

An easy, streaming way to generate XML.
Ruby
2
star
44

ohey

A rewrite of the platform detection logic in ohai, but with fewer dependencies and 100% less metaprogramming.
Ruby
2
star
45

popforms

jQuery plugin that can show any form as a modal dialog.
JavaScript
1
star
46

pet-detector

Automatic solver for Lumosity's Pet Detective game.
Ruby
1
star
47

rtl-string

Easier string manipulation for people who come from LTR backgrounds.
Ruby
1
star
48

erb2rux

An easy way to convert your ERB templates to rux.
Ruby
1
star
49

abroad

A set of parsers and serializers for dealing with localization file formats.
Ruby
1
star
50

mosaico-example

Example Rails 5 app showing how to integrate the mosaico-rails gem.
Ruby
1
star
51

range-set

An efficient set implementation that treats runs of sequential elements as ranges.
Ruby
1
star
52

jotto-client

Jotto word game iPhone app written in Ruby with RubyMotion.
Ruby
1
star
53

curdle

Programmatically remove Sorbet type annotations from Ruby code.
Ruby
1
star
54

fragmont

Font subsetting for the Rails asset pipeline.
Ruby
1
star
55

yaml-write-stream

An easy, streaming way to generate YAML.
Ruby
1
star
56

binascii

A Ruby version of Python's binascii module
Ruby
1
star
57

chopstick

An example Rails app demonstrating how to use twitter-cldr-rails to internationalize content.
Ruby
1
star
58

SmoothControls

Smooth-looking controls for Windows forms (.NET)
C#
1
star
59

camertron-rails-assets-codemirror

In-browser code editor (with a fix for PhantomJS) http://codemirror.net/
Ruby
1
star
60

rsc.js

A javascript implementation of the RSC (Reasonably Simple Computer)
JavaScript
1
star
61

rux-atom

Rux syntax highlighting for Atom.
CoffeeScript
1
star
62

commonrb

A (probably stupid) attempt at bringing CommonJs-style modules to Ruby
Ruby
1
star
63

any2tmx-web

A web front-end for the any2tmx converter that converts certain data formats to TMX (Translation Memory eXchange)
Ruby
1
star
64

any2tmx

A command-line tool to convert Rails locale-specific yaml files to the standard TMX format for translation memories.
Ruby
1
star
65

commonjs-rhino

CommonJs support for Rhino, in Ruby.
Ruby
1
star
66

bundler-tools

Ruby
1
star
67

escodegen-rb

Ruby wrapper around the escodegen JavaScript generator that generates ECMA script from an abstract syntax tree.
Ruby
1
star