├── .gitignore ├── CHANGELOG.md ├── Gemfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── activerecord-belongs_to_if.gemspec └── lib ├── active_record └── belongs_to_if │ ├── builder_extension.rb │ ├── extend.rb │ ├── preloader_extension.rb │ └── version.rb └── activerecord-belongs_to_if.rb /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /Gemfile.lock 4 | /_yardoc/ 5 | /coverage/ 6 | /doc/ 7 | /pkg/ 8 | /spec/reports/ 9 | /tmp/ 10 | *.bundle 11 | *.so 12 | *.o 13 | *.a 14 | mkmf.log 15 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.1.1 2 | 3 | - Support ActiveRecord 5.2.0 4 | 5 | ## 0.1.0 6 | 7 | - Support ActiveRecord 5.0.0 8 | 9 | ## 0.0.4 10 | - Support ActiveRecord 4.2.0 11 | 12 | ## 0.0.3 13 | - Make it compatible with Ruby 2.0.0 14 | 15 | ## 0.0.2 16 | - Accepts zero-arity block as :if option 17 | 18 | ## 0.0.1 19 | - 1st release 20 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in activerecord-may_belong_to.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Ryo Nakamura 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ActiveRecord::BelongsToIf 2 | Allows you to manage preload condition by `:if` condition. 3 | 4 | ## Usage 5 | ```rb 6 | class User < ActiveRecord::Base 7 | has_many :activities 8 | end 9 | 10 | class Activity < ActiveRecord::Base 11 | belongs_to :comment, if: -> { activity_type == "Comment" } 12 | belongs_to :issue, if: -> { activity_type == "Issue" } 13 | belongs_to :pull_request, if: -> { activity_type == "PullRequest" } 14 | end 15 | ``` 16 | 17 | ```rb 18 | user.activities.includes( 19 | [ 20 | :comment, 21 | { 22 | issue: :owner, 23 | pull_request: :owner, 24 | }, 25 | ] 26 | ) 27 | ``` 28 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | 3 | -------------------------------------------------------------------------------- /activerecord-belongs_to_if.gemspec: -------------------------------------------------------------------------------- 1 | lib = File.expand_path("../lib", __FILE__) 2 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 3 | require "active_record/belongs_to_if/version" 4 | 5 | Gem::Specification.new do |spec| 6 | spec.name = "activerecord-belongs_to_if" 7 | spec.version = ActiveRecord::BelongsToIf::VERSION 8 | spec.authors = ["Ryo Nakamura"] 9 | spec.email = ["r7kamura@gmail.com"] 10 | spec.summary = "Allows you to manage preload condition by `:if` condition." 11 | spec.homepage = "https://github.com/r7kamura/activerecord-belongs_to_if" 12 | spec.license = "MIT" 13 | 14 | spec.files = `git ls-files -z`.split("\x0") 15 | spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } 16 | spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) 17 | spec.require_paths = ["lib"] 18 | 19 | spec.add_dependency "activerecord", ">= 4.0.0" 20 | spec.add_dependency "activesupport" 21 | spec.add_development_dependency "bundler", "~> 1.7" 22 | spec.add_development_dependency "rake", "~> 10.0" 23 | end 24 | -------------------------------------------------------------------------------- /lib/active_record/belongs_to_if/builder_extension.rb: -------------------------------------------------------------------------------- 1 | module ActiveRecord 2 | module BelongsToIf 3 | module BuilderExtension 4 | # To keep compatibility with Rails > 4 5 | def valid_options 6 | super + [:if] 7 | end 8 | 9 | module ClassMethods 10 | def valid_options(options) 11 | super + [:if] 12 | end 13 | end 14 | 15 | def self.prepended(base) 16 | class << base 17 | prepend ClassMethods 18 | end 19 | end 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/active_record/belongs_to_if/extend.rb: -------------------------------------------------------------------------------- 1 | ActiveSupport.on_load(:active_record) do 2 | ActiveRecord::Associations::Builder::BelongsTo.send(:prepend, ActiveRecord::BelongsToIf::BuilderExtension) 3 | if ActiveRecord.gem_version >= Gem::Version.new('5.2.0') 4 | ActiveRecord::Associations::Preloader::Association.send(:prepend, ActiveRecord::BelongsToIf::PreloaderExtension) 5 | else 6 | ActiveRecord::Associations::Preloader::BelongsTo.send(:prepend, ActiveRecord::BelongsToIf::PreloaderExtension) 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /lib/active_record/belongs_to_if/preloader_extension.rb: -------------------------------------------------------------------------------- 1 | module ActiveRecord 2 | module BelongsToIf 3 | module PreloaderExtension 4 | # @note Overriden to use :if condition. 5 | def owners 6 | super.select do |owner| 7 | case if_condition 8 | when Proc 9 | owner.instance_exec(&if_condition) 10 | when String, Symbol 11 | owner.send(if_condition) 12 | else 13 | true 14 | end 15 | end 16 | end 17 | 18 | # @note Overridden to set loaded flag to all records. 19 | def preload(*) 20 | super.tap do 21 | @owners.each do |owner| 22 | owner.association(reflection.name).loaded! 23 | end 24 | end 25 | end 26 | 27 | private 28 | 29 | # @note Override to change `records_by_owner` so that empty array is used. 30 | def associated_records_by_owner(preloader) 31 | # Each record may have multiple owners, and vice-versa 32 | records_by_owner = Hash.new do |hash, key| 33 | hash[key] = [] 34 | end 35 | 36 | # To keep compatibility with Rails > 4 37 | if ::ActiveRecord::VERSION::MAJOR > 4 38 | records = load_records 39 | records_by_owner = Hash.new do |hash, key| 40 | hash[key] = [] 41 | end 42 | owners.each_with_object(records_by_owner) do |owner, result| 43 | result[owner] = records[convert_key(owner[owner_key_name])] || [] 44 | end 45 | else 46 | owners_map = owners_by_key 47 | owner_keys = owners_map.keys.compact 48 | 49 | 50 | if owner_keys.any? 51 | # Some databases impose a limit on the number of ids in a list (in Oracle it's 1000) 52 | # Make several smaller queries if necessary or make one query if the adapter supports it 53 | sliced = owner_keys.each_slice(klass.connection.in_clause_length || owner_keys.size) 54 | 55 | records = load_slices sliced 56 | records.each do |record, owner_key| 57 | owners_map[owner_key].each do |owner| 58 | records_by_owner[owner] << record 59 | end 60 | end 61 | end 62 | 63 | records_by_owner 64 | end 65 | end 66 | 67 | # @return [Proc, String, Symbol, nil] The value :if option passed to belongs_to. 68 | def if_condition 69 | reflection.options[:if] 70 | end 71 | end 72 | end 73 | end 74 | -------------------------------------------------------------------------------- /lib/active_record/belongs_to_if/version.rb: -------------------------------------------------------------------------------- 1 | module ActiveRecord 2 | module BelongsToIf 3 | VERSION = "0.1.1" 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /lib/activerecord-belongs_to_if.rb: -------------------------------------------------------------------------------- 1 | require "active_record" 2 | require "active_support/lazy_load_hooks" 3 | 4 | require "active_record/belongs_to_if/builder_extension" 5 | require "active_record/belongs_to_if/preloader_extension" 6 | require "active_record/belongs_to_if/extend" 7 | --------------------------------------------------------------------------------