GithubHelp home page GithubHelp logo

active_record_inherit_assoc's Introduction

ActiveRecord association inheritance

  • Makes models inherit specified attributes from an association.
  • Scope queries by inherited attributes

Install

gem install active_record_inherit_assoc

Usage

Filling inherited attributes on initialization:

# parent_name - The Symbol name of the parent association.
# options     - The Hash options to use:
#               :attr - A Symbol or an Array of Symbol names of the attributes
#                       that should be inherited from the parent association.
#
class Post < ActiveRecord::Base
  belongs_to :category
  inherits_from :category, attr: :account
end

Scoping queries

class Post < ActiveRecord::Base
  has_many :categories, inherit: :account_id
end

post = Post.first
post.categories.build.account_id == post.account_id # fills attribute on new objects
post.categories.to_sql # adds inherited attributes to queries

This is similar to adding a scope { |record| where(account_id: record.account_id) }, but also allows to do Post.all.includes(:categories) to work by filtering preloaded records. This will not use the attribute to query, so it might use a different index and find more than neccessary records.

Allowed list of values (inherit_allowed_list)

In some occasions, there are values that we don't want to filter out, even if they don't correspond to the inherited one. Following the previous Post example, this could happen if we have a universal "system" category belonging to no account, one that we associated to all the posts that have no other category. A way to keep this category (assuming that it has the account_id -1) would look like this:

class Post < ActiveRecord::Base
  has_many :categories, inherit: :account_id, inherit_allowed_list: [-1]
end

Copyright and license

Copyright 2022 Zendesk

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

Author

Ben Osheroff [email protected]

active_record_inherit_assoc's People

Contributors

bquorning avatar craig-day avatar dasch avatar gawyn avatar grosser avatar heynonster avatar kerrykdiehl avatar martinstiago avatar mervync avatar osheroff avatar pschambacher avatar razumau avatar staugaard avatar zendesk-edytaroz avatar zenspider avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

active_record_inherit_assoc's Issues

Parent inheriting child attribute breaks active record statement caching

In a project I'm working on I ran into an issue using inherit. We might be just using it wrong, or it could be an issue with active record but I thought I'd outline here in case someone could offer assistance.

The issue is when a parent model inherits an attribute from a child it appears to incorrectly cache the sql statements used to look-up the association.

I wrote a failing test in - https://github.com/zendesk/active_record_inherit_assoc/blob/master/test/test_inherit_assoc.rb

  def test_association_caching_fail
    main_1 = Main.create!(account_id: 1)
    third_1 = Third.create!(main_id: main_1.id, account_id: 1)

    assert_equal third_1, main_1.third

    main_2 = Main.create!(account_id: 2)
    third_2 = Third.create!(main_id: main_2.id, account_id: 2)

    assert_equal third_2, main_2.third # this will fail, commenting out the previous assertion will make it pass.
  end

and I wrote this gist that repros the issue in isolation.

require 'active_record'
require 'pg'
require 'active_record_inherit_assoc'

# set up database
PG_SPEC = {
  adapter:  'postgresql',
  database: 'inherit_test_db'
}

ActiveRecord::Base.establish_connection(PG_SPEC.merge('database' => 'postgres', 'schema_search_path' => 'public'))
ActiveRecord::Base.connection.drop_database PG_SPEC[:database] rescue nil
ActiveRecord::Base.connection.create_database(PG_SPEC[:database])
ActiveRecord::Base.establish_connection(PG_SPEC)

# Set active record logger to STDOUT to see executed SQL
ActiveRecord::Base.logger = Logger.new(STDOUT)

# define migration to set schema
class CreateModels < ActiveRecord::Migration
  def self.change
    create_table :users do |t|
      t.integer :some_attr

      t.timestamps
    end

    create_table :accounts do |t|
      t.integer :user_id
      t.integer :some_attr

      t.timestamps
    end
    end
end

# run migrations
CreateModels.change

# define models and populate db
class User < ActiveRecord::Base
  has_one :account, inherit: [:some_attr]
end

class Account < ActiveRecord::Base
  belongs_to :user
end

user    = User.create!(some_attr: 1)
account = Account.create!(user_id: user.id, some_attr: 1)

user.account # dirty the statment cache

user_2    = User.create!(some_attr: 2)
account_2 = Account.create!(user_id: user_2.id, some_attr: 2)

puts user_2.account.inspect # nil as the SELECT statement uses the `some_attr` value from the previous call.

I'm using v2.3.0 of active_record_inherit_assoc and v4.2.4 of activerecord.

I've tried a few other activerecord versions but haven't spent too much time trying different ones, they all seemed to fail.

In 4.2.4 it seems like there is a reflection that has an association_scope_cache https://github.com/rails/rails/blob/v4.2.4/activerecord/lib/active_record/associations/singular_association.rb#L45 and that is dirty on the second call.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.