Index
Description and History
Active Record Extended is the continuation of maintaining and improving the work done by Dan McClain, the original author of postgres_ext.
Overtime the lack of updating to support the latest versions of ActiveRecord 5.x has caused quite a bit of users forking off the project to create their own patches jobs to maintain compatibility. The only problem is that this has created a wild west of environments of sorts. The problem has grown to the point no one is attempting to directly contribute to the original source. And forked repositories are finding themselves as equally as dead with little to no activity.
Active Record Extended is intended to be a supporting community that will maintain compatibility for the foreseeable future.
Usage
Query Methods
Any
In Postgres the ANY
expression is used for gather record's that have an Array column type that contain a single matchable value within its array.
alice = User.create!(tags: [1])
bob = User.create!(tags: [1, 2])
randy = User.create!(tags: [3])
User.where.any(tags: 1) #=> [alice, bob]
This only accepts a single value. So querying for example multiple tag numbers [1,2]
will return nothing.
All
In Postgres the ALL
expression is used for gather record's that have an Array column type that contains only a single and matchable element.
alice = User.create!(tags: [1])
bob = User.create!(tags: [1, 2])
randy = User.create!(tags: [3])
User.where.all(tags: 1) #=> [alice]
This only accepts a single value to a given attribute. So querying for example multiple tag numbers [1,2]
will return nothing.
Contains
Postgres '@>' (Array type) Contains expression
Postgres '@>' (JSONB/HSTORE type) Contains expression
The contains/1
method is used for finding any elements in an Array
, JSONB
, or HSTORE
column type.
That contains all of the provided values.
Array Type:
alice = User.create!(tags: [1, 4])
bob = User.create!(tags: [3, 1])
randy = User.create!(tags: [4, 1])
User.where.contains(tags: [1, 4]) #=> [alice, randy]
HSTORE / JSONB Type:
alice = User.create!(data: { nickname: "ARExtend" })
bob = User.create!(data: { nickname: "ARExtended" })
randy = User.create!(data: { nickname: "ARExtended" })
User.where.contains(data: { nickname: "ARExtended" }) #=> [bob, randy]
Overlap
Postgres && (overlap) Expression
The overlap/1
method will match an Array column type that contains any of the provided values within its column.
alice = User.create!(tags: [1, 4])
bob = User.create!(tags: [3, 4])
randy = User.create!(tags: [4, 8])
User.where.overlap(tags: [4]) #=> [alice, bob, randy]
User.where.overlap(tags: [1, 8]) #=> [alice, randy]
User.where.overlap(tags: [1, 3, 8]) #=> [alice, bob, randy]
Inet / IP Address
Inet Contains
Postgres >> (contains) Network Expression
The inet_contains
method works by taking a column(inet type) that has a submask prepended to it.
And tries to find related records that fall within a given IP's range.
alice = User.create!(ip: "127.0.0.1/16")
bob = User.create!(ip: "192.168.0.1/16")
User.where.inet_contains(ip: "127.0.0.254") #=> [alice]
User.where.inet_contains(ip: "192.168.20.44") #=> [bob]
User.where.inet_contains(ip: "192.255.1.1") #=> []
Inet Contains or Equals
Postgres >>= (contains or equals) Network Expression
The inet_contains_or_equals
method works much like the Inet Contains method, but will also accept a submask range.
alice = User.create!(ip: "127.0.0.1/10")
bob = User.create!(ip: "127.0.0.44/24")
User.where.inet_contains_or_equals(ip: "127.0.0.1/16") #=> [alice]
User.where.inet_contains_or_equals(ip: "127.0.0.1/10") #=> [alice]
User.where.inet_contains_or_equals(ip: "127.0.0.1/32") #=> [alice, bob]
Inet Contained Within
Postgres << (contained by) Network Expression
For the inet_contained_within
method, we try to find IP's that fall within a submasking range we provide.
alice = User.create!(ip: "127.0.0.1")
bob = User.create!(ip: "127.0.0.44")
randy = User.create!(ip: "127.0.55.20")
User.where.inet_contained_within(ip: "127.0.0.1/24") #=> [alice, bob]
User.where.inet_contained_within(ip: "127.0.0.1/16") #=> [alice, bob, randy]
Inet Contained Within or Equals
Postgres <<= (contained by or equals) Network Expression
The inet_contained_within_or_equals
method works much like the Inet Contained Within method, but will also accept a submask range.
alice = User.create!(ip: "127.0.0.1/10")
bob = User.create!(ip: "127.0.0.44/32")
randy = User.create!(ip: "127.0.99.1")
User.where.inet_contained_within_or_equals(ip: "127.0.0.44/32") #=> [bob]
User.where.inet_contained_within_or_equals(ip: "127.0.0.1/16") #=> [bob, randy]
User.where.inet_contained_within_or_equals(ip: "127.0.0.44/8") #=> [alice, bob, randy]
Inet Contains or Contained Within
Postgres && (contains or is contained by) Network Expression
The inet_contains_or_contained_within
method is a combination of Inet Contains and Inet Contained Within.
It essentially (the database) tries to use both methods to find as many records as possible that match either condition on both sides.
alice = User.create!(ip: "127.0.0.1/24")
bob = User.create!(ip: "127.0.22.44/8")
randy = User.create!(ip: "127.0.99.1")
User.where.inet_contains_or_is_contained_within(ip: "127.0.255.80") #=> [bob]
User.where.inet_contains_or_is_contained_within(ip: "127.0.0.80") #=> [alice, bob]
User.where.inet_contains_or_is_contained_within(ip: "127.0.0.80/8") #=> [alice, bob, randy]
Conditional Methods
Any_of / None_of
any_of/1
simplifies the process of finding records that require multiple or
conditions.
none_of/1
is the inverse of any_of/1
. It'll find records where none of the contains are matched.
Both accepts An array of: ActiveRecord Objects, Query Strings, and basic attribute names.
Querying With Attributes:
alice = User.create!(uid: 1)
bob = User.create!(uid: 2)
randy = User.create!(uid: 3)
User.where.any_of({ uid: 1 }, { uid: 2 }) #=> [alice, bob]
Querying With ActiveRecord Objects:
alice = User.create!(uid: 1)
bob = User.create!(uid: 2)
randy = User.create!(uid: 3)
uid_one = User.where(uid: 1)
uid_two = User.where(uid: 2)
User.where.any_of(uid_one, uid_two) #=> [alice, bob]
Querying with Joined Relationships:
alice = User.create!(uid: 1)
bob = User.create!(uid: 2)
randy = User.create!(uid: 3)
tag_alice = Tag.create!(user_id: alice.id)
tag_bob = Tag.create!(user_id: bob.id)
tag_randy = Tag.create!(user_id: randy.id)
bob_tag_query = Tag.where(users: { id: bob.id }).includes(:user)
randy_tag_query = Tag.where(users: { id: randy.id }).joins(:user)
Tag.joins(:user).where.any_of(bob_tag_query, randy_tag_query) #=> [tag_bob, tag_randy] (with users table joined)
Either Join
The #either_join/2
method is a base ActiveRecord querying method that will joins records based on a set of conditionally joinable tables.
class User < ActiveRecord::Base
has_one :profile_l, class: "ProfileL"
has_one :profile_r, class: "ProfileR"
scope :completed_profile, -> { either_joins(:profile_l, :profile_r) }
end
alice = User.create!
bob = User.create!
randy = User.create! # Does not have a single completed profile type
ProfileL.create!(user_id: alice.id)
ProfileR.create!(user_id: bob.id)
User.completed_profile #=> [alice, bob]
# alternatively
User.either_joins(:profile_l, :profile_r) #=> [alice, bob]
Either Order
The #either_order/3
method is a base ActiveRecord querying method that will order a set of columns that may or may not exist for each record.
This works similar to how Either Join works. This does not however exclude records that do not have relationships.
alice = User.create!
bob = User.create!
ProfileL.create!(user_id: alice.id, left_turns: 100)
ProfileR.create!(user_id: bob.id, right_turns: 50)
User.either_order(:asc, profile_l: :left_turns, profile_r: :right_turns) #=> [bob, alice]
User.either_order(:desc, profile_l: :left_turns, profile_r: :right_turns) #=> [alice, bob]
randy = User.create!
User.either_order(:asc, profile_l: :left_turns, profile_r: :right_turns) #=> [bob, alice, randy]
User.either_order(:desc, profile_l: :left_turns, profile_r: :right_turns) #=> [randy, alice, bob]
Installation
Add this line to your application's Gemfile:
gem 'active_record_extended'
And then execute:
$ bundle
Or install it yourself as:
$ gem install active_record_extended
Development
After checking out the repo, run bin/setup
to install dependencies. Then, run rake spec
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.
License
The gem is available as open source under the terms of the MIT License.