rearmed_rails 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +6 -0
- data/LICENSE +7 -0
- data/README.md +146 -0
- data/Rakefile +33 -0
- data/lib/generators/rearmed_rails/setup_generator.rb +15 -0
- data/lib/rearmed_rails/apply_patches.rb +6 -0
- data/lib/rearmed_rails/default_enabled_patches.hash +31 -0
- data/lib/rearmed_rails/monkey_patches/minitest.rb +35 -0
- data/lib/rearmed_rails/monkey_patches/rails/active_record/base.rb +216 -0
- data/lib/rearmed_rails/monkey_patches/rails/active_record/batches.rb +60 -0
- data/lib/rearmed_rails/monkey_patches/rails/active_record/query_methods.rb +101 -0
- data/lib/rearmed_rails/monkey_patches/rails/helpers.rb +116 -0
- data/lib/rearmed_rails/monkey_patches/rails/v3.rb +58 -0
- data/lib/rearmed_rails/version.rb +3 -0
- data/lib/rearmed_rails.rb +52 -0
- data/test/tc_rearmed.rb +101 -0
- metadata +101 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a4ee0fb7927533d24c1c51d5b336ec748a2594f9
|
4
|
+
data.tar.gz: 137ddb07d7184f254f68948a2d897a4bebe20a69
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9bbfa3d1aff55e2295264ac940174b29b1bc00f1508a43fa8fb0158d8fd91437e1857b17d66b508b2ee763581debd7952a8af9dfc398c94755270e42d4204765
|
7
|
+
data.tar.gz: a1985d414a13d6bdfec7379480fd4346b345c9e1793fad0a729f58e860a8f47a98b695f60cf766412a52f906da1872cf523cdfeda2eb6be145065192bd006ef4
|
data/CHANGELOG.md
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
Copyright (c) 2017 Weston Ganger
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
4
|
+
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
6
|
+
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,146 @@
|
|
1
|
+
# Rearmed Rails
|
2
|
+
<a href='https://ko-fi.com/A5071NK' target='_blank'><img height='32' style='border:0px;height:32px;' src='https://az743702.vo.msecnd.net/cdn/kofi1.png?v=a' border='0' alt='Buy Me a Coffee' /></a>
|
3
|
+
|
4
|
+
A collection of helpful methods and monkey patches for Rails
|
5
|
+
|
6
|
+
The difference between this library and others is that all monkey patching is performed in an opt-in way because you shouldnt be using methods you dont know about anyways.
|
7
|
+
|
8
|
+
```ruby
|
9
|
+
# Gemfile
|
10
|
+
|
11
|
+
gem 'rearmed_rails'
|
12
|
+
```
|
13
|
+
|
14
|
+
Run `rails g rearmed_rails:setup` to create a settings files in `config/initializers/rearmed_rails.rb` where you can opt-in to the monkey patches available in the library. Set these values to true if you want to enable the applicable monkey patch.
|
15
|
+
|
16
|
+
```ruby
|
17
|
+
# config/initializers/rearmed.rb
|
18
|
+
|
19
|
+
RearmedRails.enabled_patches = {
|
20
|
+
rails: {
|
21
|
+
active_record: {
|
22
|
+
find_duplicates: false,
|
23
|
+
find_in_relation_batches: false,
|
24
|
+
find_or_create: false,
|
25
|
+
find_relation_each: false,
|
26
|
+
newest: false,
|
27
|
+
or: false,
|
28
|
+
pluck_to_hash: false,
|
29
|
+
pluck_to_struct: false,
|
30
|
+
reset_auto_increment: false,
|
31
|
+
reset_table: false
|
32
|
+
},
|
33
|
+
helpers: {
|
34
|
+
field_is_array: false,
|
35
|
+
link_to_confirm: false,
|
36
|
+
options_for_select_include_blank: false,
|
37
|
+
options_from_collection_for_select_include_blank: false
|
38
|
+
},
|
39
|
+
v3: {
|
40
|
+
all: false,
|
41
|
+
pluck: false,
|
42
|
+
update_columns: false
|
43
|
+
}
|
44
|
+
},
|
45
|
+
minitest: {
|
46
|
+
assert_changed: false,
|
47
|
+
assert_not_changed: false
|
48
|
+
}
|
49
|
+
}
|
50
|
+
|
51
|
+
|
52
|
+
require 'rearmed_rails/apply_patches'
|
53
|
+
```
|
54
|
+
|
55
|
+
|
56
|
+
## Rails
|
57
|
+
|
58
|
+
### ActiveRecord
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
# This version of `or` behaves way nicer than the stupid one that was implemented in Rails 5
|
62
|
+
# it allows you to do what you need when you want to. This patch is for Rails 4, 5+
|
63
|
+
Post.where(name: 'foo').or.where(content: 'bar')
|
64
|
+
Post.where(name: 'foo').or.my_custom_scope
|
65
|
+
Post.where(name: 'foo').or(Post.where(content: 'bar'))
|
66
|
+
Post.where(name: 'foo').or(content: 'bar')
|
67
|
+
|
68
|
+
Post.pluck_to_hash(:name, :category, :id)
|
69
|
+
Post.pluck_to_struct(:name, :category, :id)
|
70
|
+
|
71
|
+
Post.find_or_create(name: 'foo', content: 'bar') # use this instead of the super confusing first_or_create method
|
72
|
+
Post.find_or_create!(name: 'foo', content: 'bar')
|
73
|
+
|
74
|
+
Post.find_duplicates # return active record relation of all records that have duplicates. By default it skips the primary_key, created_at, updated_at, & deleted_at columns
|
75
|
+
Post.find_duplicates(:name) # find duplicates based on the name attribute
|
76
|
+
Post.find_duplicates(:name, :category) # find duplicates based on the name & category attribute
|
77
|
+
Post.find_duplicates(self.column_names.reject{|x| ['id','created_at','updated_at','deleted_at'].include?(x)})
|
78
|
+
|
79
|
+
# It also can delete duplicates. Valid values for keep are :first & :last. Valid values for delete_method are :destroy & :delete. soft_delete is only used if you are using acts_as_paranoid on your model.
|
80
|
+
Post.find_duplicates(:name, :category, delete: true)
|
81
|
+
Post.find_duplicates(:name, :category, delete: {keep: :first, delete_method: :destroy, soft_delete: true}) # these are the default settings for delete: true
|
82
|
+
|
83
|
+
Post.newest # get the newest post, by default ordered by :created_at
|
84
|
+
Post.newest(:updated_at) # different sort order
|
85
|
+
Post.newest(:published_at, :created_at) # multiple columns to sort on
|
86
|
+
|
87
|
+
Post.reset_table # delete all records from table and reset autoincrement column (id), works with mysql/mariadb/postgresql/sqlite
|
88
|
+
# or with options
|
89
|
+
Post.reset_table(delete_method: :destroy) # to ensure all callbacks are fired
|
90
|
+
|
91
|
+
Post.reset_auto_increment # reset mysql/mariadb/postgresql/sqlite auto-increment column, if contains records then defaults to starting from next available number
|
92
|
+
# or with options
|
93
|
+
Post.reset_auto_increment(value: 1, column: :id) # column option is only relevant for postgresql
|
94
|
+
|
95
|
+
Post.find_in_relation_batches # this returns a relation instead of an array
|
96
|
+
Post.find_relation_each # this returns a relation instead of an array
|
97
|
+
```
|
98
|
+
|
99
|
+
Note: All methods which involve deletion are compatible with Paranoia & ActsAsParanoid
|
100
|
+
|
101
|
+
### Helpers
|
102
|
+
|
103
|
+
```ruby
|
104
|
+
# field_is_array: works with field type tag, form_for, simple form, etc
|
105
|
+
= text_field_tag :name, is_array: true #=> <input type='text' name='name[]' />
|
106
|
+
|
107
|
+
# options_for_select_include_blank
|
108
|
+
options_for_select(@users.map{|x| [x.name, x.id]}, include_blank: true, selected: params[:user_id])
|
109
|
+
|
110
|
+
# options_from_collection_for_select_include_blank
|
111
|
+
options_from_collection_for_select(@users, 'id', 'name', include_blank: true, selected: params[:user_id])
|
112
|
+
|
113
|
+
# returns to rails 3 behaviour of allowing confirm attribute as well as data-confirm
|
114
|
+
= link_to 'Delete', post_path(post), method: :delete, confirm: "Are you sure you want to delete this post?"
|
115
|
+
```
|
116
|
+
|
117
|
+
### Rails 3.x Backports
|
118
|
+
```ruby
|
119
|
+
Post.all # Now returns AR relation
|
120
|
+
Post.first.update_columns(a: 'foo', b: 'bar')
|
121
|
+
Post.pluck(:name, :id) # adds multi column pluck support ex. => [['first', 1], ['second', 2], ['third', 3]]
|
122
|
+
```
|
123
|
+
|
124
|
+
### Minitest Methods
|
125
|
+
```ruby
|
126
|
+
assert_changed 'user.name' do
|
127
|
+
user.name = "Bob"
|
128
|
+
end
|
129
|
+
|
130
|
+
assert_not_changed -> { user.name } do
|
131
|
+
user.update(user_params)
|
132
|
+
end
|
133
|
+
|
134
|
+
assert_not_changed lambda{ user.name } do
|
135
|
+
user.update(user_params)
|
136
|
+
end
|
137
|
+
```
|
138
|
+
|
139
|
+
# Contributing
|
140
|
+
If you want to request a new method please raise an issue and we will discuss the idea.
|
141
|
+
|
142
|
+
|
143
|
+
# Credits
|
144
|
+
Created by Weston Ganger - @westonganger
|
145
|
+
|
146
|
+
<a href='https://ko-fi.com/A5071NK' target='_blank'><img height='32' style='border:0px;height:32px;' src='https://az743702.vo.msecnd.net/cdn/kofi1.png?v=a' border='0' alt='Buy Me a Coffee' /></a>
|
data/Rakefile
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/lib/rearmed_rails/version.rb')
|
2
|
+
require 'bundler/gem_tasks'
|
3
|
+
|
4
|
+
task :test do
|
5
|
+
require 'rake/testtask'
|
6
|
+
Rake::TestTask.new do |t|
|
7
|
+
t.libs << 'test'
|
8
|
+
t.test_files = FileList['test/**/tc_*.rb']
|
9
|
+
t.verbose = true
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
task :console do
|
14
|
+
require 'rearmed'
|
15
|
+
Rearmed.enabled_patches = {
|
16
|
+
array: true,
|
17
|
+
hash: true,
|
18
|
+
object: true,
|
19
|
+
string: true,
|
20
|
+
date: true,
|
21
|
+
enumerable: true,
|
22
|
+
rails_3: true,
|
23
|
+
rails_4: true,
|
24
|
+
rails: true,
|
25
|
+
minitest: true
|
26
|
+
}
|
27
|
+
require 'rearmed/apply_patches'
|
28
|
+
|
29
|
+
require 'irb'
|
30
|
+
binding.irb
|
31
|
+
end
|
32
|
+
|
33
|
+
task default: :test
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
|
3
|
+
module RearmedRails
|
4
|
+
class SetupGenerator < Rails::Generators::Base
|
5
|
+
|
6
|
+
def setup
|
7
|
+
create_file "config/initializers/rearmed_rails.rb", <<eos
|
8
|
+
RearmedRails.enabled_patches = #{File.read(File.join(File.dirname(__FILE__), '../../rearmed_rails/default_enabled_patches.hash'))}
|
9
|
+
|
10
|
+
require 'rearmed_rails/apply_patches'
|
11
|
+
eos
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,6 @@
|
|
1
|
+
require 'rearmed_rails/monkey_patches/rails/active_record/base'
|
2
|
+
require 'rearmed_rails/monkey_patches/rails/active_record/batches'
|
3
|
+
require 'rearmed_rails/monkey_patches/rails/active_record/query_methods'
|
4
|
+
require 'rearmed_rails/monkey_patches/rails/helpers'
|
5
|
+
require 'rearmed_rails/monkey_patches/rails/v3'
|
6
|
+
require 'rearmed_rails/monkey_patches/minitest'
|
@@ -0,0 +1,31 @@
|
|
1
|
+
{
|
2
|
+
rails: {
|
3
|
+
active_record: {
|
4
|
+
find_duplicates: false,
|
5
|
+
find_in_relation_batches: false,
|
6
|
+
find_or_create: false,
|
7
|
+
find_relation_each: false,
|
8
|
+
newest: false,
|
9
|
+
or: false,
|
10
|
+
pluck_to_hash: false,
|
11
|
+
pluck_to_struct: false,
|
12
|
+
reset_auto_increment: false,
|
13
|
+
reset_table: false
|
14
|
+
},
|
15
|
+
helpers: {
|
16
|
+
field_is_array: false,
|
17
|
+
link_to_confirm: false,
|
18
|
+
options_for_select_include_blank: false,
|
19
|
+
options_from_collection_for_select_include_blank: false
|
20
|
+
},
|
21
|
+
v3: {
|
22
|
+
all: false,
|
23
|
+
pluck: false,
|
24
|
+
update_columns: false
|
25
|
+
},
|
26
|
+
minitest: {
|
27
|
+
assert_changed: false,
|
28
|
+
assert_not_changed: false
|
29
|
+
}
|
30
|
+
}
|
31
|
+
}
|
@@ -0,0 +1,35 @@
|
|
1
|
+
if defined?(Minitest::Assertions)
|
2
|
+
|
3
|
+
enabled = RearmedRails.enabled_patches[:minitest] == true
|
4
|
+
|
5
|
+
Minitest::Assertions.module_eval do
|
6
|
+
|
7
|
+
if enabled || Rearmed.dig(Rearmed.enabled_patches, :minitest, :assert_changed)
|
8
|
+
def assert_changed(expression, &block)
|
9
|
+
if expression.respond_to?(:call)
|
10
|
+
e = expression
|
11
|
+
else
|
12
|
+
e = lambda{ block.binding.eval(expression) }
|
13
|
+
end
|
14
|
+
old = e.call
|
15
|
+
block.call
|
16
|
+
refute_equal old, e.call
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
if enabled || Rearmed.dig(Rearmed.enabled_patches, :minitest, :assert_not_changed)
|
21
|
+
def assert_not_changed(expression, &block)
|
22
|
+
if expression.respond_to?(:call)
|
23
|
+
e = expression
|
24
|
+
else
|
25
|
+
e = lambda{ block.binding.eval(expression) }
|
26
|
+
end
|
27
|
+
old = e.call
|
28
|
+
block.call
|
29
|
+
assert_equal old, e.call
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,216 @@
|
|
1
|
+
enabled = RearmedRails.enabled_patches[:rails] == true
|
2
|
+
enabled ||= RearmedRails.dig(RearmedRails.enabled_patches, :rails, :active_record) == true
|
3
|
+
|
4
|
+
if defined?(ActiveRecord)
|
5
|
+
|
6
|
+
ActiveRecord::Base.class_eval do
|
7
|
+
if enabled || RearmedRails.dig(RearmedRails.enabled_patches, :rails, :active_record, :newest)
|
8
|
+
def self.newest(*columns)
|
9
|
+
if columns.empty? || !columns.to_s.include?('created_at')
|
10
|
+
columns << 'created_at'
|
11
|
+
end
|
12
|
+
|
13
|
+
the_order = {}
|
14
|
+
columns.each do |x|
|
15
|
+
the_order[x.to_s] = :desc
|
16
|
+
end
|
17
|
+
self.order(the_order).limit(1).first
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
if enabled || RearmedRails.dig(RearmedRails.enabled_patches, :rails, :active_record, :reset_table)
|
22
|
+
def self.reset_table(opts={})
|
23
|
+
if opts[:delete_method] && opts[:delete_method].to_sym == :destroy
|
24
|
+
if self.try(:paranoid?)
|
25
|
+
self.unscoped.each do |x|
|
26
|
+
if x.respond_to?(:really_destroy!)
|
27
|
+
x.really_destroy!
|
28
|
+
else
|
29
|
+
x.destroy!
|
30
|
+
end
|
31
|
+
end
|
32
|
+
else
|
33
|
+
self.unscoped.destroy_all
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
case self.connection.adapter_name.downcase.to_sym
|
38
|
+
when :mysql2
|
39
|
+
if !opts[:delete_method] || opts[:delete_method].to_sym != :destroy
|
40
|
+
if defined?(ActsAsParanoid) && self.try(:paranoid?)
|
41
|
+
self.unscoped.delete_all!
|
42
|
+
else
|
43
|
+
self.unscoped.delete_all
|
44
|
+
end
|
45
|
+
end
|
46
|
+
self.connection.execute("ALTER TABLE #{self.table_name} AUTO_INCREMENT = 1")
|
47
|
+
when :postgresql
|
48
|
+
self.connection.execute("TRUNCATE TABLE #{self.table_name} RESTART IDENTITY")
|
49
|
+
when :sqlite3
|
50
|
+
self.connection.execute("DELETE FROM #{self.table_name}")
|
51
|
+
self.connection.execute("DELETE FROM sqlite_sequence WHERE NAME='#{self.table_name}'")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
if enabled || RearmedRails.dig(RearmedRails.enabled_patches, :rails, :active_record, :reset_auto_increment)
|
57
|
+
def self.reset_auto_increment(opts={})
|
58
|
+
case self.connection.adapter_name.downcase.to_sym
|
59
|
+
when :mysql2
|
60
|
+
opts[:value] = 1 if opts[:value].blank?
|
61
|
+
self.connection.execute("ALTER TABLE #{self.table_name} AUTO_INCREMENT = #{opts[:value]}")
|
62
|
+
when :postgresql
|
63
|
+
opts[:value] = 1 if opts[:value].blank?
|
64
|
+
self.connection.execute("ALTER SEQUENCE #{self.table_name}_#{opts[:column].to_s || self.primary_key}_seq RESTART WITH #{opts[:value]}")
|
65
|
+
when :sqlite3
|
66
|
+
opts[:value] = 0 if opts[:value].blank?
|
67
|
+
self.connection.execute("UPDATE SQLITE_SEQUENCE SET SEQ=#{opts[:value]} WHERE NAME='#{self.table_name}'")
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
if enabled || RearmedRails.dig(RearmedRails.enabled_patches, :rails, :active_record, :find_duplicates)
|
73
|
+
def self.find_duplicates(*args)
|
74
|
+
options = {}
|
75
|
+
|
76
|
+
unless args.empty?
|
77
|
+
if args.last.is_a?(Hash)
|
78
|
+
options = args.pop
|
79
|
+
end
|
80
|
+
|
81
|
+
unless args.empty?
|
82
|
+
if args.count == 1 && args.first.is_a?(Array)
|
83
|
+
args = args.first
|
84
|
+
end
|
85
|
+
|
86
|
+
options[:columns] = args
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
options[:columns] ||= self.column_names.reject{|x| [self.primary_key, :created_at, :updated_at, :deleted_at].include?(x)}
|
91
|
+
|
92
|
+
duplicates = self.select("#{options[:columns].join(', ')}, COUNT(*)").group(options[:columns]).having("COUNT(*) > 1")
|
93
|
+
|
94
|
+
if options[:delete]
|
95
|
+
if options[:delete] == true
|
96
|
+
options[:delete] = {keep: :first, delete_method: :destroy, soft_delete: true}
|
97
|
+
else
|
98
|
+
options[:delete][:delete_method] ||= :destroy
|
99
|
+
options[:delete][:keep] ||= :first
|
100
|
+
if options[:delete][:soft_delete] != false
|
101
|
+
options[:delete][:soft_delete] = true
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
if options[:delete][:keep] == :last
|
106
|
+
duplicates.reverse!
|
107
|
+
end
|
108
|
+
|
109
|
+
used = []
|
110
|
+
duplicates.reject! do |x|
|
111
|
+
attrs = x.attributes.slice(*options[:columns].collect(&:to_s))
|
112
|
+
|
113
|
+
if used.include?(attrs)
|
114
|
+
return false
|
115
|
+
else
|
116
|
+
used.push attrs
|
117
|
+
return true
|
118
|
+
end
|
119
|
+
end
|
120
|
+
used = nil
|
121
|
+
|
122
|
+
if options[:delete][:delete_method].to_sym == :delete
|
123
|
+
duplicates = self.where(id: duplicates.collect(&:id))
|
124
|
+
|
125
|
+
if x.respond_to?(:delete_all!)
|
126
|
+
duplicates.delete_all!
|
127
|
+
else
|
128
|
+
duplicates.delete_all
|
129
|
+
end
|
130
|
+
else
|
131
|
+
duplicates.each do |x|
|
132
|
+
if !options[:delete][:soft_delete] && x.respond_to?(:really_destroy!)
|
133
|
+
x.really_destroy!
|
134
|
+
else
|
135
|
+
x.destroy!
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
return nil
|
140
|
+
else
|
141
|
+
return duplicates
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
if enabled || RearmedRails.dig(RearmedRails.enabled_patches, :rails, :active_record, :find_or_create)
|
147
|
+
def self.find_or_create(attrs={}, save_opts={})
|
148
|
+
unless self.where(attrs).limit(1).first
|
149
|
+
x = self.class.new(attrs)
|
150
|
+
x.save(save_opts)
|
151
|
+
return t
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def self.find_or_create!(attrs={}, save_opts={})
|
156
|
+
unless self.where(attrs).limit(1).first
|
157
|
+
x = self.class.new(attrs)
|
158
|
+
x.save!(save_opts)
|
159
|
+
return t
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
if enabled || RearmedRails.dig(RearmedRails.enabled_patches, :rails, :active_record, :pluck_to_hash)
|
165
|
+
def self.pluck_to_hash(*keys)
|
166
|
+
hash_type = keys[-1].is_a?(Hash) ? keys.pop.fetch(:hash_type, HashWithIndifferentAccess) : HashWithIndifferentAccess
|
167
|
+
block_given = block_given?
|
168
|
+
keys, formatted_keys = format_keys(keys)
|
169
|
+
keys_one = keys.size == 1
|
170
|
+
|
171
|
+
pluck(*keys).map do |row|
|
172
|
+
value = hash_type[formatted_keys.zip(keys_one ? [row] : row)]
|
173
|
+
block_given ? yield(value) : value
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
if enabled || enabled || RearmedRails.dig(RearmedRails.enabled_patches, :rails, :active_record, :pluck_to_struct)
|
179
|
+
def self.pluck_to_struct(*keys)
|
180
|
+
struct_type = keys[-1].is_a?(Hash) ? keys.pop.fetch(:struct_type, Struct) : Struct
|
181
|
+
block_given = block_given?
|
182
|
+
keys, formatted_keys = format_keys(keys)
|
183
|
+
keys_one = keys.size == 1
|
184
|
+
|
185
|
+
struct = struct_type.new(*formatted_keys)
|
186
|
+
pluck(*keys).map do |row|
|
187
|
+
value = keys_one ? struct.new(*[row]) : struct.new(*row)
|
188
|
+
block_given ? yield(value) : value
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
private
|
194
|
+
|
195
|
+
if enabled || RearmedRails.dig(RearmedRails.enabled_patches, :rails, :active_record, :pluck_to_hash) || RearmedRails.dig(RearmedRails.enabled_patches, :rails, :pluck_to_struct)
|
196
|
+
def self.format_keys(keys)
|
197
|
+
if keys.blank?
|
198
|
+
[column_names, column_names]
|
199
|
+
else
|
200
|
+
[
|
201
|
+
keys,
|
202
|
+
keys.map do |k|
|
203
|
+
case k
|
204
|
+
when String
|
205
|
+
k.split(/\bas\b/i)[-1].strip.to_sym
|
206
|
+
when Symbol
|
207
|
+
k
|
208
|
+
end
|
209
|
+
end
|
210
|
+
]
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
enabled = RearmedRails.enabled_patches[:rails] == true
|
2
|
+
enabled ||= RearmedRails.dig(RearmedRails.enabled_patches, :rails, :active_record) == true
|
3
|
+
|
4
|
+
if defined?(ActiveRecord::Batches)
|
5
|
+
|
6
|
+
ActiveRecord::Batches.module_eval do
|
7
|
+
if enabled || RearmedRails.dig(RearmedRails.enabled_patches, :rails, :active_record, :find_in_relation_batches)
|
8
|
+
def find_in_relation_batches(options = {})
|
9
|
+
options.assert_valid_keys(:start, :batch_size)
|
10
|
+
|
11
|
+
relation = self
|
12
|
+
start = options[:start]
|
13
|
+
batch_size = options[:batch_size] || 1000
|
14
|
+
|
15
|
+
unless block_given?
|
16
|
+
return to_enum(:find_in_relation_batches, options) do
|
17
|
+
total = start ? where(table[primary_key].gteq(start)).size : size
|
18
|
+
(total - 1).div(batch_size) + 1
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
if logger && (arel.orders.present? || arel.taken.present?)
|
23
|
+
logger.warn("Scoped order and limit are ignored, it's forced to be batch order and batch size")
|
24
|
+
end
|
25
|
+
|
26
|
+
relation = relation.reorder(batch_order).limit(batch_size)
|
27
|
+
#records = start ? relation.where(table[primary_key].gteq(start)).to_a : relation.to_a
|
28
|
+
records = start ? relation.where(table[primary_key].gteq(start)) : relation
|
29
|
+
|
30
|
+
while records.any?
|
31
|
+
records_size = records.size
|
32
|
+
primary_key_offset = records.last.id
|
33
|
+
raise ActiveRecordError, "Primary key not included in the custom select clause" unless primary_key_offset
|
34
|
+
|
35
|
+
yield records
|
36
|
+
|
37
|
+
break if records_size < batch_size
|
38
|
+
|
39
|
+
records = relation.where(table[primary_key].gt(primary_key_offset))#.to_a
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
if enabled || RearmedRails.dig(RearmedRails.enabled_patches, :rails, :active_record, :find_relation_each)
|
45
|
+
def find_relation_each(options = {})
|
46
|
+
if block_given?
|
47
|
+
find_in_relation_batches(options) do |records|
|
48
|
+
records.each { |record| yield record }
|
49
|
+
end
|
50
|
+
else
|
51
|
+
enum_for :find_relation_each, options do
|
52
|
+
options[:start] ? where(table[primary_key].gteq(options[:start])).size : size
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
enabled = RearmedRails.enabled_patches[:rails] == true
|
2
|
+
enabled ||= RearmedRails.dig(RearmedRails.enabled_patches, :rails, :active_record) == true
|
3
|
+
|
4
|
+
if defined?(ActiveRecord)
|
5
|
+
|
6
|
+
if enabled || RearmedRails.dig(RearmedRails.enabled_patches, :rails, :active_record, :or)
|
7
|
+
|
8
|
+
if Rails::VERSION::MAJOR > 4
|
9
|
+
|
10
|
+
module RearmedRails
|
11
|
+
class OrChain
|
12
|
+
def initialize(scope)
|
13
|
+
@scope = scope
|
14
|
+
end
|
15
|
+
|
16
|
+
def method_missing(method, *args, &block)
|
17
|
+
other = @scope.klass.unscoped do
|
18
|
+
@scope.klass.send(method, *args, &block)
|
19
|
+
end
|
20
|
+
return @scope.smart_or(other)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
ActiveRecord::QueryMethods.class_eval do
|
26
|
+
def or(opts=nil, *rest)
|
27
|
+
if opts.nil?
|
28
|
+
return RearmedRails::OrChain.new(self)
|
29
|
+
else
|
30
|
+
other = opts.is_a?(ActiveRecord::Relation) ? opts : klass.unscoped.where(opts, rest)
|
31
|
+
|
32
|
+
self.where_clause = self.where_clause.or(other.where_clause)
|
33
|
+
self.having_clause = self.having_clause.or(other.having_clause)
|
34
|
+
|
35
|
+
return self
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
else # rails 4
|
41
|
+
|
42
|
+
module ActiveRecord
|
43
|
+
module Querying
|
44
|
+
delegate :or, to: :all
|
45
|
+
end
|
46
|
+
|
47
|
+
module QueryMethods
|
48
|
+
class OrChain
|
49
|
+
def initialize(scope)
|
50
|
+
@scope = scope
|
51
|
+
end
|
52
|
+
|
53
|
+
def method_missing(method, *args, &block)
|
54
|
+
right_relation = @scope.klass.unscoped do
|
55
|
+
@scope.klass.send(method, *args, &block)
|
56
|
+
end
|
57
|
+
@scope.or(right_relation)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def or(opts = :chain, *rest)
|
62
|
+
if opts == :chain
|
63
|
+
OrChain.new(self)
|
64
|
+
else
|
65
|
+
left = self
|
66
|
+
right = (ActiveRecord::Relation === opts) ? opts : klass.unscoped.where(opts, rest)
|
67
|
+
|
68
|
+
unless left.where_values.empty? || right.where_values.empty?
|
69
|
+
left.where_values = [left.where_ast.or(right.where_ast)]
|
70
|
+
right.where_values = []
|
71
|
+
end
|
72
|
+
|
73
|
+
left = left.merge(right)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
private # Returns an Arel AST containing only where_values
|
78
|
+
|
79
|
+
def where_ast
|
80
|
+
arel_wheres = []
|
81
|
+
|
82
|
+
where_values.each do |where|
|
83
|
+
arel_wheres << (String === where ? Arel.sql(where) : where)
|
84
|
+
end
|
85
|
+
|
86
|
+
return Arel::Nodes::Grouping.new(Arel::Nodes::And.new(arel_wheres)) if arel_wheres.length >= 2
|
87
|
+
|
88
|
+
if Arel::Nodes::SqlLiteral === arel_wheres.first
|
89
|
+
Arel::Nodes::Grouping.new(arel_wheres.first)
|
90
|
+
else
|
91
|
+
arel_wheres.first
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
enabled = RearmedRails.enabled_patches[:rails] == true
|
2
|
+
enabled ||= RearmedRails.dig(RearmedRails.enabled_patches, :rails, :helpers) == true
|
3
|
+
|
4
|
+
if defined?(ActionView::Helpers)
|
5
|
+
|
6
|
+
if enabled || RearmedRails.dig(RearmedRails.enabled_patches, :rails, :helpers, :link_to_confirm)
|
7
|
+
ActionView::Helpers::UrlHelper.module_eval do
|
8
|
+
def convert_options_to_data_attributes(options, html_options)
|
9
|
+
if html_options
|
10
|
+
html_options = html_options.stringify_keys
|
11
|
+
html_options['data-remote'] = 'true' if link_to_remote_options?(options) || link_to_remote_options?(html_options)
|
12
|
+
|
13
|
+
method = html_options.delete('method')
|
14
|
+
add_method_to_attributes!(html_options, method) if method
|
15
|
+
|
16
|
+
### CUSTOM - behave like Rails 3.2
|
17
|
+
confirm = html_options.delete('confirm')
|
18
|
+
html_options['data-confirm'] = confirm if confirm
|
19
|
+
|
20
|
+
html_options
|
21
|
+
else
|
22
|
+
link_to_remote_options?(options) ? {'data-remote' => 'true'} : {}
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
if enabled || RearmedRails.dig(RearmedRails.enabled_patches, :rails, :helpers, :field_is_array)
|
29
|
+
module ActionView
|
30
|
+
module Helpers
|
31
|
+
module Tags
|
32
|
+
class Base
|
33
|
+
private
|
34
|
+
|
35
|
+
original_method = instance_method(:add_default_name_and_id)
|
36
|
+
define_method :add_default_name_and_id do |options|
|
37
|
+
original_method.bind(self).(options)
|
38
|
+
|
39
|
+
if options['is_array'] && options['name']
|
40
|
+
options['name'] = "#{options['name']}[]"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
module RearmedRails
|
50
|
+
module RailsHelpers
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
RearmedRails::RailsHelpers.module_eval do
|
55
|
+
|
56
|
+
if enabled || RearmedRails.dig(RearmedRails.enabled_patches, :rails, :helpers, :options_for_select_include_blank)
|
57
|
+
def options_for_select(container, selected = nil)
|
58
|
+
return container if String === container
|
59
|
+
|
60
|
+
if selected.is_a?(Hash)
|
61
|
+
include_blank = selected[:include_blank] || selected['include_blank']
|
62
|
+
end
|
63
|
+
|
64
|
+
selected, disabled = extract_selected_and_disabled(selected).map do |r|
|
65
|
+
Array(r).map(&:to_s)
|
66
|
+
end
|
67
|
+
|
68
|
+
options = []
|
69
|
+
|
70
|
+
if include_blank
|
71
|
+
include_blank = '' if include_blank == true
|
72
|
+
options.push(content_tag_string(:option, include_blank, {value: ''}))
|
73
|
+
end
|
74
|
+
|
75
|
+
container.each do |element|
|
76
|
+
html_attributes = option_html_attributes(element)
|
77
|
+
text, value = option_text_and_value(element).map(&:to_s)
|
78
|
+
|
79
|
+
html_attributes[:selected] ||= option_value_selected?(value, selected)
|
80
|
+
html_attributes[:disabled] ||= disabled && option_value_selected?(value, disabled)
|
81
|
+
html_attributes[:value] = value
|
82
|
+
|
83
|
+
options.push content_tag_string(:option, text, html_attributes)
|
84
|
+
end
|
85
|
+
|
86
|
+
options.join("\n").html_safe
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
if enabled || RearmedRails.dig(RearmedRails.enabled_patches, :rails, :helpers, :options_from_collection_for_select_include_blank)
|
91
|
+
def options_from_collection_for_select(collection, value_method, text_method, selected = nil)
|
92
|
+
options = collection.map do |element|
|
93
|
+
[value_for_collection(element, text_method), value_for_collection(element, value_method), option_html_attributes(element)]
|
94
|
+
end
|
95
|
+
|
96
|
+
if selected.is_a?(Hash)
|
97
|
+
include_blank = selected[:include_blank] || selected['include_blank']
|
98
|
+
end
|
99
|
+
|
100
|
+
selected, disabled = extract_selected_and_disabled(selected)
|
101
|
+
|
102
|
+
select_deselect = {
|
103
|
+
selected: extract_values_from_collection(collection, value_method, selected),
|
104
|
+
disabled: extract_values_from_collection(collection, value_method, disabled),
|
105
|
+
include_blank: include_blank
|
106
|
+
}
|
107
|
+
|
108
|
+
options_for_select(options, select_deselect)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
ActiveSupport.on_load :action_view do
|
114
|
+
ActionView::Base.send(:include, RearmedRails::RailsHelpers)
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
enabled = RearmedRails.enabled_patches[:rails] == true
|
2
|
+
enabled ||= RearmedRails.dig(RearmedRails.enabled_patches, :rails, :v3) == true
|
3
|
+
|
4
|
+
if defined?(ActiveRecord) && ActiveRecord::VERSION::MAJOR < 4
|
5
|
+
|
6
|
+
if enabled || RearmedRails.dig(RearmedRails.enabled_patches, :rails, :v3, :all)
|
7
|
+
ActiveRecord::FinderMethods.module_eval do
|
8
|
+
def all(*args)
|
9
|
+
args.any? ? apply_finder_options(args.first) : self
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
if enabled || RearmedRails.dig(RearmedRails.enabled_patches, :rails, :v3, :pluck)
|
15
|
+
ActiveRecord::Relation.class_eval do
|
16
|
+
def pluck(*args)
|
17
|
+
args.map! do |column_name|
|
18
|
+
if column_name.is_a?(Symbol) && column_names.include?(column_name.to_s)
|
19
|
+
"#{connection.quote_table_name(table_name)}.#{connection.quote_column_name(column_name)}"
|
20
|
+
else
|
21
|
+
column_name.to_s
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
relation = clone
|
26
|
+
relation.select_values = args
|
27
|
+
klass.connection.select_all(relation.arel).map! do |attributes|
|
28
|
+
initialized_attributes = klass.initialize_attributes(attributes)
|
29
|
+
attributes.map do |key, attr|
|
30
|
+
klass.type_cast_attribute(key, initialized_attributes)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
if enabled || RearmedRails.dig(RearmedRails.enabled_patches, :rails, :v3, :update_columns)
|
38
|
+
ActiveRecord::Persistence::ClassMethods.module_eval do
|
39
|
+
def update_columns(attributes)
|
40
|
+
raise ActiveRecordError, "cannot update a new record" if new_record?
|
41
|
+
raise ActiveRecordError, "cannot update a destroyed record" if destroyed?
|
42
|
+
|
43
|
+
attributes.each_key do |key|
|
44
|
+
raise ActiveRecordError, "#{key.to_s} is marked as readonly" if self.class.readonly_attributes.include?(key.to_s)
|
45
|
+
end
|
46
|
+
|
47
|
+
updated_count = self.class.unscoped.where(self.class.primary_key => id).update_all(attributes)
|
48
|
+
|
49
|
+
attributes.each do |k, v|
|
50
|
+
raw_write_attribute(k, v)
|
51
|
+
end
|
52
|
+
|
53
|
+
updated_count == 1
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'rearmed_rails/version'
|
2
|
+
|
3
|
+
module RearmedRails
|
4
|
+
|
5
|
+
@enabled_patches = eval(File.read(File.join(File.dirname(__FILE__), 'rearmed_rails/default_enabled_patches.hash')))
|
6
|
+
|
7
|
+
def self.enabled_patches=(val)
|
8
|
+
@enabled_patches = val
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.enabled_patches
|
12
|
+
@enabled_patches
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def self.dig(collection, *values)
|
18
|
+
current_val = nil
|
19
|
+
current_collection = collection
|
20
|
+
values.each_with_index do |val,i|
|
21
|
+
if i+1 == values.length
|
22
|
+
if (current_collection.is_a?(Array) && val.is_a?(Integer)) || (current_collection.is_a?(Hash) && ['String','Symbol'].include?(val.class.name))
|
23
|
+
current_val = current_collection[val]
|
24
|
+
else
|
25
|
+
current_val = nil
|
26
|
+
end
|
27
|
+
elsif current_collection.is_a?(Array)
|
28
|
+
if val.is_a?(Integer)
|
29
|
+
current_collection = current_collection[val]
|
30
|
+
next
|
31
|
+
else
|
32
|
+
current_val = nil
|
33
|
+
break
|
34
|
+
end
|
35
|
+
elsif current_collection.is_a?(Hash)
|
36
|
+
if ['Symbol','String'].include?(val.class.name)
|
37
|
+
current_collection = current_collection[val]
|
38
|
+
next
|
39
|
+
else
|
40
|
+
current_val = nil
|
41
|
+
break
|
42
|
+
end
|
43
|
+
else
|
44
|
+
current_val = nil
|
45
|
+
break
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
return current_val
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
data/test/tc_rearmed.rb
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
#!/usr/bin/env ruby -w
|
2
|
+
|
3
|
+
lib = File.expand_path('../lib', __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
|
6
|
+
require 'yaml'
|
7
|
+
require 'minitest'
|
8
|
+
|
9
|
+
require 'rearmed_rails'
|
10
|
+
|
11
|
+
require 'minitest/autorun'
|
12
|
+
|
13
|
+
class TestRearmedRails < MiniTest::Test
|
14
|
+
def setup
|
15
|
+
Minitest::Assertions.module_eval do
|
16
|
+
alias_method :eql, :assert_equal
|
17
|
+
end
|
18
|
+
|
19
|
+
RearmedRails.enabled_patches = {
|
20
|
+
rails: true,
|
21
|
+
minitest: true
|
22
|
+
}
|
23
|
+
require 'rearmed_rails/apply_patches'
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_minitest
|
27
|
+
str = 'first'
|
28
|
+
assert_changed "str" do
|
29
|
+
str = 'second'
|
30
|
+
end
|
31
|
+
|
32
|
+
str = 'first'
|
33
|
+
assert_changed ->{ str } do
|
34
|
+
str = 'second'
|
35
|
+
end
|
36
|
+
|
37
|
+
name = 'first'
|
38
|
+
assert_changed lambda{ name } do
|
39
|
+
name = 'second'
|
40
|
+
end
|
41
|
+
|
42
|
+
name = 'first'
|
43
|
+
assert_not_changed 'name' do
|
44
|
+
name = 'first'
|
45
|
+
end
|
46
|
+
|
47
|
+
name = 'first'
|
48
|
+
assert_not_changed ->{ name } do
|
49
|
+
name = 'first'
|
50
|
+
end
|
51
|
+
|
52
|
+
name = 'first'
|
53
|
+
assert_not_changed lambda{ name } do
|
54
|
+
name = 'first'
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_general_rails
|
59
|
+
# THE MOST IMPORTANT TESTS HERE WOULD BE dedupe, reset_auto_increment, reset_table
|
60
|
+
|
61
|
+
#Post.pluck_to_hash(:name, :category, :id)
|
62
|
+
#Post.pluck_to_struct(:name, :category, :id)
|
63
|
+
|
64
|
+
#Post.find_or_create(name: 'foo', content: 'bar') # use this instead of the super confusing first_or_create method
|
65
|
+
#Post.find_or_create!(name: 'foo', content: 'bar')
|
66
|
+
|
67
|
+
#Post.find_duplicates # return active record relation of all records that have duplicates
|
68
|
+
#Post.find_duplicates(:name) # find duplicates based on the name attribute
|
69
|
+
#Post.find_duplicates([:name, :category]) # find duplicates based on the name & category attribute
|
70
|
+
#Post.find_duplicates(name: 'A Specific Name')
|
71
|
+
|
72
|
+
#Post.reset_table # delete all records from table and reset autoincrement column (id), works with mysql/mariadb/postgresql/sqlite
|
73
|
+
## or with options
|
74
|
+
#Post.reset_table(delete_method: :destroy) # to ensure all callbacks are fired
|
75
|
+
|
76
|
+
#Post.reset_auto_increment # reset mysql/mariadb/postgresql/sqlite auto-increment column, if contains records then defaults to starting from next available number
|
77
|
+
## or with options
|
78
|
+
#Post.reset_auto_increment(value: 1, column: :id) # column option is only relevant for postgresql
|
79
|
+
|
80
|
+
#Post.find_in_relation_batches # this returns a relation instead of an array
|
81
|
+
#Post.find_relation_each # this returns a relation instead of an array
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_rails_3
|
85
|
+
#my_hash.compact
|
86
|
+
#my_hash.compact!
|
87
|
+
#Post.all # Now returns AR relation
|
88
|
+
#Post.first.update_columns(a: 'foo', b: 'bar')
|
89
|
+
#Post.pluck(:name, :id) # adds multi column pluck support ex. => [['first', 1], ['second', 2], ['third', 3]]
|
90
|
+
end
|
91
|
+
|
92
|
+
def test_rails_4
|
93
|
+
#Post.where(name: 'foo').or.where(content: 'bar')
|
94
|
+
#Post.where(name: 'foo').or.my_custom_scope
|
95
|
+
#Post.where(name: 'foo').or(Post.where(content: 'bar'))
|
96
|
+
#Post.where(name: 'foo).or(content: 'bar')
|
97
|
+
|
98
|
+
#= link_to 'Delete', post_path(post), method: :delete, confirm: "Are you sure you want to delete this post?"
|
99
|
+
# returns to rails 3 behaviour of allowing confirm attribute as well as data-confirm
|
100
|
+
end
|
101
|
+
end
|
metadata
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rearmed_rails
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Weston Ganger
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-02-28 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: minitest
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description: A collection of helpful methods and monkey patches for Rails
|
56
|
+
email: westonganger@gmail.com
|
57
|
+
executables: []
|
58
|
+
extensions: []
|
59
|
+
extra_rdoc_files: []
|
60
|
+
files:
|
61
|
+
- CHANGELOG.md
|
62
|
+
- LICENSE
|
63
|
+
- README.md
|
64
|
+
- Rakefile
|
65
|
+
- lib/generators/rearmed_rails/setup_generator.rb
|
66
|
+
- lib/rearmed_rails.rb
|
67
|
+
- lib/rearmed_rails/apply_patches.rb
|
68
|
+
- lib/rearmed_rails/default_enabled_patches.hash
|
69
|
+
- lib/rearmed_rails/monkey_patches/minitest.rb
|
70
|
+
- lib/rearmed_rails/monkey_patches/rails/active_record/base.rb
|
71
|
+
- lib/rearmed_rails/monkey_patches/rails/active_record/batches.rb
|
72
|
+
- lib/rearmed_rails/monkey_patches/rails/active_record/query_methods.rb
|
73
|
+
- lib/rearmed_rails/monkey_patches/rails/helpers.rb
|
74
|
+
- lib/rearmed_rails/monkey_patches/rails/v3.rb
|
75
|
+
- lib/rearmed_rails/version.rb
|
76
|
+
- test/tc_rearmed.rb
|
77
|
+
homepage: https://github.com/westonganger/rearmed_rails
|
78
|
+
licenses: []
|
79
|
+
metadata: {}
|
80
|
+
post_install_message:
|
81
|
+
rdoc_options: []
|
82
|
+
require_paths:
|
83
|
+
- lib
|
84
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: 1.9.3
|
89
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
requirements: []
|
95
|
+
rubyforge_project:
|
96
|
+
rubygems_version: 2.6.8
|
97
|
+
signing_key:
|
98
|
+
specification_version: 4
|
99
|
+
summary: A collection of helpful methods and monkey patches for Rails
|
100
|
+
test_files:
|
101
|
+
- test/tc_rearmed.rb
|