Skip to content

Instantly share code, notes, and snippets.

@JoshCheek
Created January 20, 2017 14:48
Show Gist options
  • Save JoshCheek/a8af39e4c57fe245b735c41d605bceb1 to your computer and use it in GitHub Desktop.
Save JoshCheek/a8af39e4c57fe245b735c41d605bceb1 to your computer and use it in GitHub Desktop.
# > Note 1: Yes, the parenthesis `( )`` for the prepend call are required when
# > using this inline style. If you do not use the parenthesis it will fail
# > silently and the patch will not be applied.
# -- https://solidfoundationwebdev.com/blog/posts/writing-clean-monkey-patches-fixing-kaminari-1-0-0-argumenterror-comparison-of-fixnum-with-string-failed
#
# The code below will show what's happening in this line of code from the blog:
#
# prepend(KaminariFix = Module.new do
# ...
# end)
def a(h) {a: block_given?, **h} end # This corresponds to `prepend`
def b() {b: block_given?} end # This corresponds to `Module.new`
# The structure of your code can be recreated like this,
# which uses parentheses to send the block to `b`
a(b() do end) # => {:a=>false, :b=>true}
# The other way to use parentheses here would send the block to `a`
a(b()) do end # => {:a=>true, :b=>false}
# Without parens, do/end blocks behave like the second example,
# binding to the first possible method call, which is why you added parens
a b do end # => {:a=>true, :b=>false}
# Curly braces are the other way to define a block
a(b()) { } # => {:a=>true, :b=>false}
a(b() { }) # => {:a=>false, :b=>true}
# Without parentheses, curly braces bind to the closest possible method call
a b { } # => {:a=>false, :b=>true}
# So in your code, any of these will work
a(b() do end) # => {:a=>false, :b=>true}
a(b() { }) # => {:a=>false, :b=>true}
a b { } # => {:a=>false, :b=>true}
# ===== Restore the old behaviour =====
# Undo the patch that fixes the bug: https://github.com/kaminari/kaminari/commit/01ec38a1ca2114a7ef819986304df4060571bed3
require 'kaminari/version'
Kaminari::VERSION # => "1.0.1"
Kaminari::VERSION = '1.0.0' # => "1.0.0"
require 'kaminari'
gem_path = $LOAD_PATH.find { |p| p =~ /kaminari-core/ }
file_path = File.join gem_path, 'kaminari/models/page_scope_methods.rb'
body = File.read file_path
body.gsub! /(?<=default_per_page\)).to_i$/, ''
eval body
# ===== Something to paginate =====
require 'active_record'
ActiveRecord::Base.establish_connection adapter: 'sqlite3', database: ':memory:'
ActiveRecord::Schema.verbose = false
ActiveRecord::Schema.create_table(:users) { |t| t.string :name }
User = Class.new ActiveRecord::Base
7.times { |i| User.create! name: "User#{i}" }
# ===== Before the patch =====
# This line comes from the test to expose the bug: https://github.com/kaminari/kaminari/commit/01ec38a1ca2114a7ef819986304df4060571bed3#diff-e0776ea1e9927826ff1ece0d4b6a36d8R89
User.page(1).per('5').load.total_count rescue $! # => #<ArgumentError: comparison of Integer with String failed>
# ===== Apply the patch =====
require 'kaminari/version'
if Kaminari::VERSION == "1.0.0"
module Kaminari::PageScopeMethods # module keyword ensures KaminariFix is namespaced
prepend KaminariFix = Module.new { # curly braces send the block to `Module.new` instead of `prepend`
def per(num, *rest, &b)
super(num.to_i, *rest, &b) # this might work, at the very least it works for our example
end
}
end
end
# ===== After the patch =====
# Same line we ran in "Before the patch" but now it works
after = User.page(1).per('5').load.total_count # => 7
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment