active_record_tweaks 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +7 -1
- data/.rubocop.yml +803 -0
- data/.travis.yml +17 -7
- data/Appraisals +26 -12
- data/Gemfile +1 -1
- data/README.md +36 -20
- data/Rakefile +5 -2
- data/active_record_tweaks.gemspec +11 -7
- data/gemfiles/rails_3_2.gemfile +10 -0
- data/gemfiles/rails_4_0.gemfile +10 -0
- data/gemfiles/rails_4_1.gemfile +10 -0
- data/gemfiles/rails_4_2.gemfile +10 -0
- data/lib/active_record_tweaks.rb +9 -7
- data/lib/active_record_tweaks/integration.rb +68 -53
- data/lib/active_record_tweaks/version.rb +5 -1
- data/spec/active_record_tweaks_spec.rb +228 -143
- data/spec/spec_helper.rb +14 -16
- metadata +69 -59
- data/gemfiles/rails3_2.gemfile +0 -8
- data/gemfiles/rails4_0.gemfile +0 -8
- data/gemfiles/rails4_1.gemfile +0 -8
data/.travis.yml
CHANGED
@@ -1,13 +1,23 @@
|
|
1
|
+
# Send builds to container-based infrastructure
|
2
|
+
# http://docs.travis-ci.com/user/workers/container-based-infrastructure/
|
3
|
+
sudo: false
|
1
4
|
language: ruby
|
2
5
|
cache:
|
3
6
|
- bundler
|
4
7
|
rvm:
|
5
|
-
- 1.
|
6
|
-
- 2.
|
7
|
-
|
8
|
-
|
8
|
+
- 2.1.10
|
9
|
+
- 2.2.5
|
10
|
+
# Since the Travis Build Env does not recognize `2.3` alias yet
|
11
|
+
# We need to specify the version precisely
|
12
|
+
- 2.3.1
|
13
|
+
- ruby-head
|
9
14
|
gemfile:
|
10
|
-
- gemfiles/
|
11
|
-
- gemfiles/
|
12
|
-
- gemfiles/
|
15
|
+
- gemfiles/rails_3_2.gemfile
|
16
|
+
- gemfiles/rails_4_0.gemfile
|
17
|
+
- gemfiles/rails_4_1.gemfile
|
18
|
+
- gemfiles/rails_4_2.gemfile
|
19
|
+
matrix:
|
20
|
+
fast_finish: true
|
21
|
+
allow_failures:
|
22
|
+
- rvm: ruby-head
|
13
23
|
|
data/Appraisals
CHANGED
@@ -1,18 +1,32 @@
|
|
1
1
|
|
2
|
-
appraise "
|
3
|
-
version = "3.2.
|
4
|
-
gem
|
5
|
-
gem
|
2
|
+
appraise "rails_3_2" do
|
3
|
+
version = "~> 3.2.20"
|
4
|
+
gem "activesupport", version
|
5
|
+
gem "actionpack", version
|
6
|
+
gem "activerecord", version
|
7
|
+
gem "railties", version
|
6
8
|
end
|
7
9
|
|
8
|
-
appraise "
|
9
|
-
version = "4.0.
|
10
|
-
gem
|
11
|
-
gem
|
10
|
+
appraise "rails_4_0" do
|
11
|
+
version = "~> 4.0.12"
|
12
|
+
gem "activesupport", version
|
13
|
+
gem "actionpack", version
|
14
|
+
gem "activerecord", version
|
15
|
+
gem "railties", version
|
12
16
|
end
|
13
17
|
|
14
|
-
appraise "
|
15
|
-
version = "4.1.
|
16
|
-
gem
|
17
|
-
gem
|
18
|
+
appraise "rails_4_1" do
|
19
|
+
version = "~> 4.1.8"
|
20
|
+
gem "activesupport", version
|
21
|
+
gem "actionpack", version
|
22
|
+
gem "activerecord", version
|
23
|
+
gem "railties", version
|
24
|
+
end
|
25
|
+
|
26
|
+
appraise "rails_4_2" do
|
27
|
+
version = "~> 4.2.0"
|
28
|
+
gem "activesupport", version
|
29
|
+
gem "actionpack", version
|
30
|
+
gem "activerecord", version
|
31
|
+
gem "railties", version
|
18
32
|
end
|
data/Gemfile
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
source
|
1
|
+
source "https://rubygems.org"
|
2
2
|
gemspec
|
data/README.md
CHANGED
@@ -1,39 +1,42 @@
|
|
1
|
-
Active Record Tweaks
|
2
|
-
===========
|
1
|
+
# Active Record Tweaks
|
3
2
|
|
4
3
|
Active Record is great, but could be better. Here are some tweaks for it.
|
5
4
|
|
6
|
-
### Support
|
7
|
-
===========
|
8
|
-
Tested against:
|
9
|
-
- Active Record of version `3.2` and `4.0`
|
10
|
-
- Ruby `1.9.3`, `2.0.0` (except Rails 4 with `1.9.2`)
|
11
5
|
|
12
|
-
|
13
|
-
[](http://badge.fury.io/rb/active_record_tweaks)
|
14
|
-
[](https://gemnasium.com/PikachuEXE/active_record_tweaks)
|
15
|
-
[](https://coveralls.io/r/PikachuEXE/active_record_tweaks)
|
16
|
-
[](https://codeclimate.com/github/PikachuEXE/active_record_tweaks)
|
6
|
+
## Status
|
17
7
|
|
18
|
-
|
19
|
-
|
8
|
+
[](https://travis-ci.org/PikachuEXE/active_record_tweaks)
|
9
|
+
[](http://badge.fury.io/rb/active_record_tweaks)
|
10
|
+
[](https://gemnasium.com/PikachuEXE/active_record_tweaks)
|
11
|
+
[](https://coveralls.io/r/PikachuEXE/active_record_tweaks)
|
12
|
+
[](https://codeclimate.com/github/PikachuEXE/active_record_tweaks)
|
13
|
+
|
14
|
+
|
15
|
+
## Installation
|
20
16
|
|
21
17
|
```ruby
|
22
18
|
gem 'active_record_tweaks'
|
23
19
|
```
|
24
20
|
|
25
|
-
|
26
|
-
|
21
|
+
|
22
|
+
## Usage
|
27
23
|
|
28
24
|
Either include it in specific record or just `ActiveRecord::Base`
|
29
25
|
```ruby
|
30
26
|
class SomeRecord
|
27
|
+
include ActiveRecordTweaks::Integration::InstanceMethods
|
28
|
+
# This module is also DEPRECATED
|
29
|
+
# See below for details
|
30
|
+
extend ActiveRecordTweaks::Integration::ClassMethods
|
31
|
+
|
32
|
+
# DEPRECATED
|
31
33
|
include ActiveRecordTweaks
|
32
34
|
end
|
33
35
|
|
34
36
|
# or
|
35
37
|
|
36
38
|
# In a initialzer
|
39
|
+
# DEPRECATED
|
37
40
|
ActiveRecord::Base.send(:include, ActiveRecordTweaks)
|
38
41
|
```
|
39
42
|
|
@@ -54,15 +57,23 @@ Nothing special, just like `record.cache_key` in rails 4.1
|
|
54
57
|
But it does not check against columns
|
55
58
|
e.g. When you have some virtual timestamp attribute method (cached or not)
|
56
59
|
Just make sure you throw some name to it or it will raise error
|
57
|
-
Alias: `#cache_key_from_attribute`
|
60
|
+
Alias: `#cache_key_from_attribute`
|
58
61
|
Usage:
|
59
62
|
```ruby
|
60
63
|
# Just like using #cache_key
|
61
|
-
record.cache_key_from_attributes(:happy_at, :
|
64
|
+
record.cache_key_from_attributes(:happy_at, :children_max_updated_at)
|
62
65
|
```
|
63
66
|
|
64
67
|
|
65
68
|
### `.cache_key`
|
69
|
+
|
70
|
+
**DEPRECATED**
|
71
|
+
This method does NOT consider the query like filters and and sort orders.
|
72
|
+
Thus deprecated without replacement.
|
73
|
+
Rails 5 already have `#cache_key` in relation class: https://github.com/rails/rails/pull/20884
|
74
|
+
There is also a gem for older rails: https://github.com/customink/activerecord-collection_cache_key
|
75
|
+
|
76
|
+
**Usage**
|
66
77
|
There is no class level cache key for ActiveRecord at the moment (4.0.1)
|
67
78
|
Passing an array to `cache_digest` could lead to performance issue and the key can become too long when collection is big
|
68
79
|
([rails#12726](https://github.com/rails/rails/pull/12726))
|
@@ -98,6 +109,11 @@ RecordClass.cache_key(:updated_at, :updated_on)
|
|
98
109
|
|
99
110
|
|
100
111
|
### `.cache_key_without_timestamp`
|
112
|
+
|
113
|
+
**DEPRECATED**
|
114
|
+
Same deprecation reasons and replacement suggestion as `.cache_key` above
|
115
|
+
|
116
|
+
**Usage**
|
101
117
|
Just like `.cache_key(nil)`
|
102
118
|
But much clearer
|
103
119
|
```ruby
|
@@ -106,8 +122,8 @@ Person.maximum(:updated_at) # => 20131106012125528738000
|
|
106
122
|
Person.cache_key_without_timestamp # => "people/all/1000"
|
107
123
|
|
108
124
|
# Other examples
|
109
|
-
Product.
|
110
|
-
Product.
|
125
|
+
Product.cache_key_without_timestamp # => "products/all/0" (empty, has updated timestamp columns or not)
|
126
|
+
Product.cache_key_without_timestamp # => "products/all/1" (not empty but has no updated timestamp columns)
|
111
127
|
```
|
112
128
|
Usage:
|
113
129
|
```ruby
|
data/Rakefile
CHANGED
@@ -1,15 +1,18 @@
|
|
1
1
|
require "appraisal"
|
2
2
|
require "bundler"
|
3
3
|
require "rspec/core/rake_task"
|
4
|
+
require "rubocop/rake_task"
|
4
5
|
|
5
6
|
Bundler::GemHelper.install_tasks
|
6
7
|
|
7
8
|
RSpec::Core::RakeTask.new(:spec)
|
8
9
|
|
10
|
+
RuboCop::RakeTask.new(:rubocop)
|
11
|
+
|
9
12
|
if !ENV["APPRAISAL_INITIALIZED"] && !ENV["TRAVIS"]
|
10
13
|
task :default do
|
11
|
-
sh "
|
14
|
+
sh "appraisal install && rake appraisal spec rubocop"
|
12
15
|
end
|
13
16
|
else
|
14
|
-
task :
|
17
|
+
task default: [:spec, :rubocop]
|
15
18
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
-
|
2
|
+
# rubocop:disable all
|
3
|
+
$LOAD_PATH.push File.expand_path("../lib", __FILE__)
|
3
4
|
|
4
5
|
author_name = "PikachuEXE"
|
5
6
|
gem_name = "active_record_tweaks"
|
@@ -11,7 +12,9 @@ Gem::Specification.new do |s|
|
|
11
12
|
s.name = gem_name
|
12
13
|
s.version = ActiveRecordTweaks::VERSION
|
13
14
|
s.summary = "Some Tweaks for ActiveRecord"
|
14
|
-
s.description =
|
15
|
+
s.description = <<-DOC
|
16
|
+
ActiveRecord is great, but could be better. Here are some tweaks for it.
|
17
|
+
DOC
|
15
18
|
|
16
19
|
s.license = "MIT"
|
17
20
|
|
@@ -21,21 +24,22 @@ Gem::Specification.new do |s|
|
|
21
24
|
|
22
25
|
s.files = `git ls-files`.split("\n")
|
23
26
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
24
|
-
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
27
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
25
28
|
s.require_paths = ["lib"]
|
26
29
|
|
27
30
|
s.add_dependency "activerecord", ">= 3.2.0", "< 5.0.0"
|
28
|
-
s.add_dependency "activesupport", ">= 3.2.0", "< 5.0.0"
|
29
31
|
|
30
32
|
s.add_development_dependency "bundler", ">= 1.0.0"
|
31
|
-
s.add_development_dependency "rake", "
|
32
|
-
s.add_development_dependency "appraisal", "
|
33
|
-
s.add_development_dependency "rspec", "~>
|
33
|
+
s.add_development_dependency "rake", "~> 10.0"
|
34
|
+
s.add_development_dependency "appraisal", "~> 2.0"
|
35
|
+
s.add_development_dependency "rspec", "~> 3.0"
|
36
|
+
s.add_development_dependency "rspec-its", "~> 1.0"
|
34
37
|
s.add_development_dependency "sqlite3", ">= 1.3"
|
35
38
|
s.add_development_dependency "database_cleaner", ">= 1.0"
|
36
39
|
s.add_development_dependency "coveralls", ">= 0.7"
|
37
40
|
s.add_development_dependency "gem-release", ">= 0.7"
|
38
41
|
s.add_development_dependency "timecop", ">= 0.7.1"
|
42
|
+
s.add_development_dependency "rubocop", "~> 0.30"
|
39
43
|
|
40
44
|
s.required_rubygems_version = ">= 1.4.0"
|
41
45
|
end
|
data/lib/active_record_tweaks.rb
CHANGED
@@ -1,12 +1,14 @@
|
|
1
|
-
require
|
2
|
-
|
3
|
-
require 'active_record_tweaks/version'
|
4
|
-
require 'active_record_tweaks/integration'
|
1
|
+
require "active_record_tweaks/version"
|
2
|
+
require "active_record_tweaks/integration"
|
5
3
|
|
6
4
|
module ActiveRecordTweaks
|
7
|
-
|
5
|
+
def self.included(base)
|
6
|
+
# rubocop:disable all
|
7
|
+
warn "[DEPRECATION] including `ActiveRecordTweaks` is deprecated. Please see README for recommanded usage."
|
8
|
+
# rubocop:enable all
|
8
9
|
|
9
|
-
|
10
|
-
|
10
|
+
base.class_eval do
|
11
|
+
include Integration
|
12
|
+
end
|
11
13
|
end
|
12
14
|
end
|
@@ -1,73 +1,88 @@
|
|
1
|
-
require 'active_support/concern'
|
2
|
-
|
3
1
|
module ActiveRecordTweaks
|
4
2
|
module Integration
|
5
|
-
|
3
|
+
def self.included(base)
|
4
|
+
base.class_eval do
|
5
|
+
# rubocop:disable all
|
6
|
+
warn "[DEPRECATION] including `ActiveRecordTweaks::Integration` is deprecated. Please see README for recommanded usage."
|
7
|
+
# rubocop:enable all
|
6
8
|
|
7
|
-
|
8
|
-
|
9
|
-
# (e.g. Cookie based caching with expiration )
|
10
|
-
#
|
11
|
-
# Product.new.cache_key_without_timestamp # => "products/new"
|
12
|
-
# Product.find(5).cache_key_without_timestamp # => "products/5" (updated_at not available)
|
13
|
-
# Person.find(5).cache_key_without_timestamp # => "people/5" (updated_at available)
|
14
|
-
def cache_key_without_timestamp
|
15
|
-
case
|
16
|
-
when new_record?
|
17
|
-
"#{self.class.model_name.cache_key}/new"
|
18
|
-
else
|
19
|
-
"#{self.class.model_name.cache_key}/#{id}"
|
9
|
+
extend ClassMethods
|
10
|
+
include InstanceMethods
|
20
11
|
end
|
21
12
|
end
|
22
13
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
14
|
+
module InstanceMethods
|
15
|
+
# Returns a cache key that can be used to identify this record.
|
16
|
+
# Timestamp is not used to allow custom caching expiration
|
17
|
+
# (e.g. Cookie based caching with expiration )
|
18
|
+
#
|
19
|
+
# Product.new.cache_key_without_timestamp # => "products/new"
|
20
|
+
# Product.find(5).cache_key_without_timestamp # => "products/5" (updated_at not available)
|
21
|
+
# Person.find(5).cache_key_without_timestamp # => "people/5" (updated_at available)
|
22
|
+
def cache_key_without_timestamp
|
23
|
+
if new_record?
|
24
|
+
"#{self.class.model_name.cache_key}/new"
|
25
|
+
else
|
26
|
+
"#{self.class.model_name.cache_key}/#{id}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Works like #cache_key in rails 4.1, but does not check column
|
31
|
+
# Useful when you have some virtual timestamp attribute method (cached or not)
|
32
|
+
#
|
33
|
+
# @param attribute_names [Array<Symbol,String>]
|
34
|
+
# Names of attributes method(s)
|
35
|
+
# It does not have to be column(s)
|
36
|
+
#
|
37
|
+
# @raise [ArgumentError] when attribute_names is empty
|
38
|
+
def cache_key_from_attributes(*attribute_names)
|
39
|
+
attribute_names.any? || fail(ArgumentError)
|
33
40
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
41
|
+
timestamp = max_updated_attribute_timestamp_for_cache_key(attribute_names)
|
42
|
+
if timestamp
|
43
|
+
timestamp = timestamp.utc.to_s(cache_timestamp_format)
|
44
|
+
"#{self.class.model_name.cache_key}/#{id}-#{timestamp}"
|
45
|
+
else
|
46
|
+
"#{self.class.model_name.cache_key}/#{id}"
|
47
|
+
end
|
39
48
|
end
|
40
|
-
|
41
|
-
alias_method :cache_key_from_attribute, :cache_key_from_attributes
|
49
|
+
alias cache_key_from_attribute cache_key_from_attributes
|
42
50
|
|
43
|
-
|
51
|
+
private
|
44
52
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
53
|
+
def max_updated_attribute_timestamp_for_cache_key(timestamp_attribute_names)
|
54
|
+
timestamps = timestamp_attribute_names.map do |attribute_name|
|
55
|
+
send(attribute_name)
|
56
|
+
end.compact
|
49
57
|
|
50
|
-
|
51
|
-
timestamps.map
|
58
|
+
return nil unless timestamps.present?
|
59
|
+
timestamps.map(&:to_time).max
|
52
60
|
end
|
53
61
|
end
|
54
62
|
|
55
63
|
module ClassMethods
|
64
|
+
# rubocop:disable all
|
65
|
+
warn "[DEPRECATION] `ActiveRecordTweaks::Integration::ClassMethods` is deprecated without replacement. Please read README in project for details."
|
66
|
+
# rubocop:enable all
|
67
|
+
|
56
68
|
# Returns a cache key for the ActiveRecord class based
|
57
69
|
# based on count and maximum value of update timestamp columns
|
58
70
|
# (e.g. Cookie based caching with expiration)
|
59
71
|
#
|
60
|
-
#
|
61
|
-
# Product.cache_key
|
62
|
-
#
|
72
|
+
# @example when record class is empty and has updated timestamp columns or not
|
73
|
+
# Product.cache_key # => "products/all/0"
|
74
|
+
# @example when record class is not empty but has no updated timestamp columns
|
75
|
+
# Product.cache_key # => "products/all/1"
|
76
|
+
# @example when record class is not empty and has updated timestamp columns
|
77
|
+
# Person.cache_key # => "people/all/1-20071224150000"
|
63
78
|
#
|
64
79
|
# @param [Array<String, Symbol>] args The column name with timestamp to check
|
65
80
|
def cache_key(*args)
|
66
81
|
timestamp_columns = args.empty? ? [:updated_at] : args
|
67
82
|
|
68
|
-
if timestamp = max_updated_column_timestamp_for_cache_key(timestamp_columns)
|
83
|
+
if (timestamp = max_updated_column_timestamp_for_cache_key(timestamp_columns))
|
69
84
|
timestamp = timestamp.utc.to_s(cache_timestamp_format)
|
70
|
-
"#{
|
85
|
+
"#{model_name.cache_key}/all/#{count}-#{timestamp}"
|
71
86
|
else
|
72
87
|
cache_key_without_timestamp
|
73
88
|
end
|
@@ -76,23 +91,23 @@ module ActiveRecordTweaks
|
|
76
91
|
# Returns a cache key for the ActiveRecord class based
|
77
92
|
# based on count only
|
78
93
|
#
|
79
|
-
# Product.cache_key
|
80
|
-
# Product.cache_key
|
81
|
-
# Person.cache_key
|
94
|
+
# Product.cache_key # => "products/all/0" (empty, has updated timestamp columns or not)
|
95
|
+
# Product.cache_key # => "products/all/1" (not empty but has no updated timestamp columns)
|
96
|
+
# Person.cache_key # => "people/all/1" (not empty and has updated timestamp columns)
|
82
97
|
#
|
83
98
|
# @param [Array<String, Symbol>] args The column name with timestamp to check
|
84
99
|
def cache_key_without_timestamp
|
85
|
-
"#{
|
100
|
+
"#{model_name.cache_key}/all/#{count}"
|
86
101
|
end
|
87
102
|
|
88
103
|
private
|
89
104
|
|
90
105
|
def max_updated_column_timestamp_for_cache_key(timestamp_columns)
|
91
|
-
available_timestamp_columns = timestamp_columns.select { |c|
|
106
|
+
available_timestamp_columns = timestamp_columns.select { |c| column_names.include?(c.to_s) }
|
107
|
+
timestamps = available_timestamp_columns.map { |column| maximum(column) }.compact
|
92
108
|
|
93
|
-
|
94
|
-
|
95
|
-
end
|
109
|
+
return nil unless timestamps.present?
|
110
|
+
timestamps.map(&:to_time).max
|
96
111
|
end
|
97
112
|
end
|
98
113
|
end
|