api_hammer 0.2.2 → 0.3.0
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/CHANGELOG.md +3 -0
- data/Rakefile.rb +2 -1
- data/lib/api_hammer/active_record_cache_find_by.rb +135 -0
- data/lib/api_hammer/version.rb +1 -1
- data/test/active_record_cache_find_by_test.rb +201 -0
- data/test/halt_test.rb +2 -4
- data/test/helper.rb +4 -0
- metadata +47 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9927a5896d97412a1668d60c7f341649cca1232c
|
4
|
+
data.tar.gz: 0d65a4cf1399613c3f1433f0a456f88ae1cf9008
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1f1fdf86e116a112eee8a09420b62d356ce66ac8522e41ef30470862ed7c29491e0cbe7fa02e5fe7b63bb834887d572a68feeeb5fc9ab5eede0db6ddebe3e95c
|
7
|
+
data.tar.gz: 51a41edd33b376e0d2cc9e49ff86fa0d60afa7cb8dbda2d4ebac12b10f64c300002813db33f79d7f914d52a9f9636f87c11bea584aa06fcf081705b81e96ecb6
|
data/CHANGELOG.md
CHANGED
data/Rakefile.rb
CHANGED
@@ -0,0 +1,135 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
class Relation
|
5
|
+
if !method_defined?(:first_without_caching)
|
6
|
+
alias_method :first_without_caching, :first
|
7
|
+
def first(*args)
|
8
|
+
one_record_with_caching(args.empty?) { first_without_caching(*args) }
|
9
|
+
end
|
10
|
+
end
|
11
|
+
if !method_defined?(:take_without_caching) && method_defined?(:take)
|
12
|
+
alias_method :take_without_caching, :take
|
13
|
+
def take(*args)
|
14
|
+
one_record_with_caching(args.empty?) { take_without_caching(*args) }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# retrieves one record, hitting the cache if appropriate. the argument may bypass caching
|
19
|
+
# (the caller could elect to just not call this method if caching is to be avoided, but since this
|
20
|
+
# method already builds in opting whether or not to hit cache, the code is simpler just passing that in).
|
21
|
+
#
|
22
|
+
# requires a block which returns the record
|
23
|
+
def one_record_with_caching(can_cache = true)
|
24
|
+
actual_right = proc do |where_value|
|
25
|
+
if where_value.right.is_a?(Arel::Nodes::BindParam)
|
26
|
+
column, value = bind_values.detect { |(column, value)| column.name == where_value.left.name }
|
27
|
+
value
|
28
|
+
else
|
29
|
+
where_value.right
|
30
|
+
end
|
31
|
+
end
|
32
|
+
cache_find_bys = klass.send(:cache_find_bys)
|
33
|
+
can_cache &&= cache_find_bys &&
|
34
|
+
!loaded? && # if it's loaded no need to hit cache
|
35
|
+
where_values.all? { |wv| wv.is_a?(Arel::Nodes::Equality) } && # no inequality or that sort of thing
|
36
|
+
cache_find_bys.include?(where_values.map { |wv| wv.left.name }.sort) && # any of the set of where-values to cache match this relation
|
37
|
+
where_values.map(&actual_right).all? { |r| r.is_a?(String) || r.is_a?(Numeric) } && # check all right side values are simple types, number or string
|
38
|
+
offset_value.nil? &&
|
39
|
+
joins_values.blank? &&
|
40
|
+
order_values.blank? &&
|
41
|
+
!reverse_order_value &&
|
42
|
+
includes_values.blank? &&
|
43
|
+
preload_values.blank? &&
|
44
|
+
select_values.blank? &&
|
45
|
+
group_values.blank? &&
|
46
|
+
from_value.nil? &&
|
47
|
+
lock_value.nil?
|
48
|
+
|
49
|
+
if can_cache
|
50
|
+
cache_key = klass.send(:cache_key_for, where_values.map { |wv| [wv.left.name, actual_right.call(wv)] })
|
51
|
+
klass.finder_cache.fetch(cache_key) do
|
52
|
+
yield
|
53
|
+
end
|
54
|
+
else
|
55
|
+
yield
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
class Base
|
61
|
+
class << self
|
62
|
+
def finder_cache=(val)
|
63
|
+
define_singleton_method(:finder_cache) { val }
|
64
|
+
end
|
65
|
+
|
66
|
+
# the cache. should be an instance of some sort of ActiveSupport::Cache::Store.
|
67
|
+
# by default uses Rails.cache if that exists, or creates a ActiveSupport::Cache::MemoryStore to use.
|
68
|
+
# set this per-model or on ActiveRecord::Base, as needed; it is inherited.
|
69
|
+
def finder_cache
|
70
|
+
# dummy; this gets set below
|
71
|
+
end
|
72
|
+
|
73
|
+
# causes requests to retrieve a record by the given attributes (all of them) to be cached.
|
74
|
+
# this is for single records only. it is unsafe to use with a set of attributes whose values
|
75
|
+
# (in conjunction) may be associated with multiple records.
|
76
|
+
#
|
77
|
+
# see .finder_cache and .find_cache= for where it is cached.
|
78
|
+
#
|
79
|
+
# #flush_find_cache is defined on the instance. it is called on save to clear an updated record from
|
80
|
+
# the cache. it may also be called explicitly to clear a record from the cache.
|
81
|
+
#
|
82
|
+
# beware of multiple application servers with different caches - a record cached in multiple will not
|
83
|
+
# be invalidated in all when it is saved in one.
|
84
|
+
def cache_find_by(*attribute_names)
|
85
|
+
unless cache_find_bys
|
86
|
+
# initial setup
|
87
|
+
self.cache_find_bys = Set.new
|
88
|
+
after_update :flush_find_cache
|
89
|
+
before_destroy :flush_find_cache
|
90
|
+
end
|
91
|
+
|
92
|
+
find_by = attribute_names.map do |name|
|
93
|
+
raise(ArgumentError) unless name.is_a?(Symbol) || name.is_a?(String)
|
94
|
+
name.to_s.dup.freeze
|
95
|
+
end.sort.freeze
|
96
|
+
|
97
|
+
self.cache_find_bys = (cache_find_bys | [find_by]).freeze
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
def cache_find_bys=(val)
|
102
|
+
define_singleton_method(:cache_find_bys) { val }
|
103
|
+
singleton_class.send(:private, :cache_find_bys)
|
104
|
+
end
|
105
|
+
|
106
|
+
def cache_find_bys
|
107
|
+
nil
|
108
|
+
end
|
109
|
+
|
110
|
+
def cache_key_for(find_attributes)
|
111
|
+
attrs = find_attributes.map { |k,v| [k.to_s, v.to_s] }.sort_by(&:first).inject([], &:+)
|
112
|
+
cache_key_prefix = ['cache_find_by', table_name]
|
113
|
+
@parser ||= URI.const_defined?(:Parser) ? URI::Parser.new : URI
|
114
|
+
cache_key = (cache_key_prefix + attrs).map do |part|
|
115
|
+
@parser.escape(part, /[^a-z0-9\-\.\_\~]/i)
|
116
|
+
end.join('/')
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# the above dummy method has no content because we want to evaluate this now, not in the method, to
|
121
|
+
# avoid instantiating duplicate MemoryStores.
|
122
|
+
self.finder_cache = (Object.const_defined?(:Rails) && ::Rails.cache) || ::ActiveSupport::Cache::MemoryStore.new
|
123
|
+
|
124
|
+
# clears this record from the cache used by cache_find_by
|
125
|
+
def flush_find_cache
|
126
|
+
self.class.send(:cache_find_bys).each do |attribute_names|
|
127
|
+
find_attributes = attribute_names.map { |attr_name| [attr_name, attribute_was(attr_name)] }
|
128
|
+
self.class.instance_exec(find_attributes) do |find_attributes|
|
129
|
+
finder_cache.delete(cache_key_for(find_attributes))
|
130
|
+
end
|
131
|
+
end
|
132
|
+
nil
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
data/lib/api_hammer/version.rb
CHANGED
@@ -0,0 +1,201 @@
|
|
1
|
+
proc { |p| $:.unshift(p) unless $:.any? { |lp| File.expand_path(lp) == p } }.call(File.expand_path('.', File.dirname(__FILE__)))
|
2
|
+
require 'helper'
|
3
|
+
|
4
|
+
require 'active_support/cache'
|
5
|
+
require 'active_record'
|
6
|
+
|
7
|
+
ActiveRecord::Base.establish_connection(
|
8
|
+
:adapter => "sqlite3",
|
9
|
+
:database => ":memory:"
|
10
|
+
)
|
11
|
+
|
12
|
+
module Rails
|
13
|
+
class << self
|
14
|
+
def cache
|
15
|
+
@cache ||= ActiveSupport::Cache::MemoryStore.new
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
require 'api_hammer/active_record_cache_find_by'
|
21
|
+
|
22
|
+
ActiveRecord::Schema.define do
|
23
|
+
create_table :albums do |table|
|
24
|
+
table.column :title, :string
|
25
|
+
table.column :performer, :string
|
26
|
+
table.column :tracks, :integer
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class Album < ActiveRecord::Base
|
31
|
+
cache_find_by(:id)
|
32
|
+
cache_find_by(:performer)
|
33
|
+
cache_find_by(:title, :performer)
|
34
|
+
cache_find_by(:tracks)
|
35
|
+
end
|
36
|
+
|
37
|
+
class VinylAlbum < Album
|
38
|
+
self.finder_cache = ActiveSupport::Cache::MemoryStore.new
|
39
|
+
end
|
40
|
+
|
41
|
+
describe 'ActiveRecord::Base.cache_find_by' do
|
42
|
+
def assert_caches(key, cache = Rails.cache)
|
43
|
+
assert !cache.read(key), "cache already contains a key #{key}: #{cache.read(key)}"
|
44
|
+
yield
|
45
|
+
ensure
|
46
|
+
assert cache.read(key), "key #{key} was not cached"
|
47
|
+
end
|
48
|
+
|
49
|
+
def assert_not_caches(key, cache = Rails.cache)
|
50
|
+
assert !cache.read(key), "cache already contains a key #{key}: #{cache.read(key)}"
|
51
|
+
yield
|
52
|
+
ensure
|
53
|
+
assert !cache.read(key), "key was incorrectly cached - #{key}: #{cache.read(key)}"
|
54
|
+
end
|
55
|
+
|
56
|
+
after do
|
57
|
+
Album.all.each(&:destroy)
|
58
|
+
end
|
59
|
+
|
60
|
+
it('caches #find by primary key') do
|
61
|
+
id = Album.create!.id
|
62
|
+
assert_caches("cache_find_by/albums/id/#{id}") { assert Album.find(id) }
|
63
|
+
end
|
64
|
+
|
65
|
+
it('caches #find_by_id') do
|
66
|
+
id = Album.create!.id
|
67
|
+
assert_caches("cache_find_by/albums/id/#{id}") { assert Album.find_by_id(id) }
|
68
|
+
end
|
69
|
+
|
70
|
+
it('caches #where.first with primary key') do
|
71
|
+
id = Album.create!.id
|
72
|
+
assert_caches("cache_find_by/albums/id/#{id}") { assert Album.where(:id => id).first }
|
73
|
+
end
|
74
|
+
|
75
|
+
it('caches find_by_x with one attribute') do
|
76
|
+
Album.create!(:performer => 'x')
|
77
|
+
assert_caches("cache_find_by/albums/performer/x") { assert Album.find_by_performer('x') }
|
78
|
+
end
|
79
|
+
|
80
|
+
it('caches find_by_x! with one attribute') do
|
81
|
+
Album.create!(:performer => 'x')
|
82
|
+
assert_caches("cache_find_by/albums/performer/x") { assert Album.find_by_performer!('x') }
|
83
|
+
end
|
84
|
+
|
85
|
+
it('caches where.first with one attribute') do
|
86
|
+
Album.create!(:performer => 'x')
|
87
|
+
assert_caches("cache_find_by/albums/performer/x") { assert Album.where(:performer => 'x').first }
|
88
|
+
end
|
89
|
+
|
90
|
+
it('caches where.first! with one attribute') do
|
91
|
+
Album.create!(:performer => 'x')
|
92
|
+
assert_caches("cache_find_by/albums/performer/x") { assert Album.where(:performer => 'x').first! }
|
93
|
+
end
|
94
|
+
|
95
|
+
it('caches #where.first with integer attribute') do
|
96
|
+
id = Album.create!(:tracks => 3).id
|
97
|
+
assert_caches("cache_find_by/albums/tracks/3") { assert Album.where(:tracks => 3).first }
|
98
|
+
end
|
99
|
+
|
100
|
+
it('does not cache #where.first with inequality of integer attribute') do
|
101
|
+
id = Album.create!(:tracks => 3).id
|
102
|
+
assert_not_caches("cache_find_by/albums/tracks/3") { assert Album.where(Album.arel_table['tracks'].gteq(3)).first }
|
103
|
+
end
|
104
|
+
|
105
|
+
if ActiveRecord::Relation.method_defined?(:take)
|
106
|
+
it('caches where.take with one attribute') do
|
107
|
+
Album.create!(:performer => 'x')
|
108
|
+
assert_caches("cache_find_by/albums/performer/x") { assert Album.where(:performer => 'x').take }
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
it('does not cache where.last with one attribute') do
|
113
|
+
Album.create!(:performer => 'x')
|
114
|
+
assert_not_caches("cache_find_by/albums/performer/x") { assert Album.where(:performer => 'x').last }
|
115
|
+
end
|
116
|
+
|
117
|
+
it('does not cache find with array') do
|
118
|
+
ids = [Album.create!.id, Album.create!.id]
|
119
|
+
assert_not_caches("cache_find_by/albums/id/#{ids.first}") { assert Album.find(ids) }
|
120
|
+
end
|
121
|
+
|
122
|
+
it('does not cache find_by_x with array') do
|
123
|
+
ids = [Album.create!.id, Album.create!.id]
|
124
|
+
assert_not_caches("cache_find_by/albums/id/#{ids.first}") { assert Album.find_by_id(ids) }
|
125
|
+
end
|
126
|
+
|
127
|
+
it('does not cache where.first with array') do
|
128
|
+
ids = [Album.create!.id, Album.create!.id]
|
129
|
+
assert_not_caches("cache_find_by/albums/id/#{ids.first}") { assert Album.where(:id => ids).first }
|
130
|
+
end
|
131
|
+
|
132
|
+
it('does not cache find_by_x with one attribute') do
|
133
|
+
Album.create!(:title => 'x')
|
134
|
+
assert_not_caches("cache_find_by/albums/title/x") { assert Album.find_by_title('x') }
|
135
|
+
end
|
136
|
+
|
137
|
+
it('does not cache where.first with one attribute') do
|
138
|
+
Album.create!(:title => 'x')
|
139
|
+
assert_not_caches("cache_find_by/albums/title/x") { assert Album.where(:title => 'x').first }
|
140
|
+
end
|
141
|
+
|
142
|
+
it('caches find_by_x with two attributes') do
|
143
|
+
Album.create!(:title => 'x', :performer => 'y')
|
144
|
+
assert_caches("cache_find_by/albums/performer/y/title/x") { assert Album.find_by_title_and_performer('x', 'y') }
|
145
|
+
end
|
146
|
+
|
147
|
+
it('caches where.first with two attributes') do
|
148
|
+
Album.create!(:title => 'x', :performer => 'y')
|
149
|
+
assert_caches("cache_find_by/albums/performer/y/title/x") { assert Album.where(:title => 'x', :performer => 'y').first }
|
150
|
+
end
|
151
|
+
|
152
|
+
it('flushes cache on save') do
|
153
|
+
album = Album.create!(:title => 'x', :performer => 'y')
|
154
|
+
assert_caches(key1 = "cache_find_by/albums/performer/y/title/x") { assert Album.find_by_title_and_performer('x', 'y') }
|
155
|
+
assert_caches(key2 = "cache_find_by/albums/performer/y") { assert Album.find_by_performer('y') }
|
156
|
+
album.update_attributes!(:performer => 'z')
|
157
|
+
assert !Rails.cache.read(key1), Rails.cache.instance_eval { @data }.inspect
|
158
|
+
assert !Rails.cache.read(key2), Rails.cache.instance_eval { @data }.inspect
|
159
|
+
end
|
160
|
+
|
161
|
+
it('flushes cache on destroy') do
|
162
|
+
album = Album.create!(:title => 'x', :performer => 'y')
|
163
|
+
assert_caches(key1 = "cache_find_by/albums/performer/y/title/x") { assert Album.find_by_title_and_performer('x', 'y') }
|
164
|
+
assert_caches(key2 = "cache_find_by/albums/performer/y") { assert Album.find_by_performer('y') }
|
165
|
+
album.destroy
|
166
|
+
assert !Rails.cache.read(key1), Rails.cache.instance_eval { @data }.inspect
|
167
|
+
assert !Rails.cache.read(key2), Rails.cache.instance_eval { @data }.inspect
|
168
|
+
end
|
169
|
+
|
170
|
+
it 'inherits cache_find_bys' do
|
171
|
+
assert VinylAlbum.send(:cache_find_bys).any? { |f| f == ['id'] }
|
172
|
+
end
|
173
|
+
|
174
|
+
it 'uses a different cache when specified' do
|
175
|
+
assert Album.finder_cache != VinylAlbum.finder_cache
|
176
|
+
|
177
|
+
id = Album.create!.id
|
178
|
+
key = "cache_find_by/albums/id/#{id}"
|
179
|
+
assert_caches(key) do
|
180
|
+
assert_not_caches(key, VinylAlbum.finder_cache) do
|
181
|
+
assert Album.find(id)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
id = VinylAlbum.create!.id
|
186
|
+
key = "cache_find_by/albums/id/#{id}"
|
187
|
+
assert_caches(key, VinylAlbum.finder_cache) do
|
188
|
+
assert_not_caches(key) do
|
189
|
+
assert VinylAlbum.find(id)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
it 'does not get confused by values with slashes' do
|
195
|
+
Album.create!(:title => 'z', :performer => 'y/title/x')
|
196
|
+
Album.create!(:title => 'x', :performer => 'y')
|
197
|
+
|
198
|
+
Album.where(:performer => 'y', :title => 'x').first
|
199
|
+
assert_equal 'z', Album.where(:performer => 'y/title/x').first.title
|
200
|
+
end
|
201
|
+
end
|
data/test/halt_test.rb
CHANGED
@@ -36,10 +36,8 @@ describe 'ApiHammer::Rails#halt' do
|
|
36
36
|
it 'returns a record if it exists' do
|
37
37
|
record = Object.new
|
38
38
|
model = Class.new do
|
39
|
-
(
|
40
|
-
|
41
|
-
define_method(:table_name) { 'records' }
|
42
|
-
end
|
39
|
+
define_singleton_method(:where) { |attrs| [record] }
|
40
|
+
define_singleton_method(:table_name) { 'records' }
|
43
41
|
end
|
44
42
|
assert_equal record, FakeController.new.find_or_halt(model, {:id => 'anid'})
|
45
43
|
end
|
data/test/helper.rb
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
proc { |p| $:.unshift(p) unless $:.any? { |lp| File.expand_path(lp) == p } }.call(File.expand_path('../lib', File.dirname(__FILE__)))
|
2
2
|
|
3
|
+
require 'bundler'
|
4
|
+
Bundler.setup
|
5
|
+
|
3
6
|
require 'simplecov'
|
7
|
+
require 'byebug'
|
4
8
|
|
5
9
|
# NO EXPECTATIONS
|
6
10
|
ENV["MT_NO_EXPECTATIONS"] = ''
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: api_hammer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ethan
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-06-
|
11
|
+
date: 2014-06-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -164,6 +164,48 @@ dependencies:
|
|
164
164
|
- - ">="
|
165
165
|
- !ruby/object:Gem::Version
|
166
166
|
version: '0'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: activesupport
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - ">="
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
type: :development
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - ">="
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
181
|
+
- !ruby/object:Gem::Dependency
|
182
|
+
name: activerecord
|
183
|
+
requirement: !ruby/object:Gem::Requirement
|
184
|
+
requirements:
|
185
|
+
- - ">="
|
186
|
+
- !ruby/object:Gem::Version
|
187
|
+
version: '0'
|
188
|
+
type: :development
|
189
|
+
prerelease: false
|
190
|
+
version_requirements: !ruby/object:Gem::Requirement
|
191
|
+
requirements:
|
192
|
+
- - ">="
|
193
|
+
- !ruby/object:Gem::Version
|
194
|
+
version: '0'
|
195
|
+
- !ruby/object:Gem::Dependency
|
196
|
+
name: sqlite3
|
197
|
+
requirement: !ruby/object:Gem::Requirement
|
198
|
+
requirements:
|
199
|
+
- - ">="
|
200
|
+
- !ruby/object:Gem::Version
|
201
|
+
version: '0'
|
202
|
+
type: :development
|
203
|
+
prerelease: false
|
204
|
+
version_requirements: !ruby/object:Gem::Requirement
|
205
|
+
requirements:
|
206
|
+
- - ">="
|
207
|
+
- !ruby/object:Gem::Version
|
208
|
+
version: '0'
|
167
209
|
description: actually a set of small API-related tools. very much unlike a hammer
|
168
210
|
at all, which is one large tool.
|
169
211
|
email:
|
@@ -181,6 +223,7 @@ files:
|
|
181
223
|
- Rakefile.rb
|
182
224
|
- bin/hc
|
183
225
|
- lib/api_hammer.rb
|
226
|
+
- lib/api_hammer/active_record_cache_find_by.rb
|
184
227
|
- lib/api_hammer/check_required_params.rb
|
185
228
|
- lib/api_hammer/faraday/outputter.rb
|
186
229
|
- lib/api_hammer/halt.rb
|
@@ -196,6 +239,7 @@ files:
|
|
196
239
|
- lib/api_hammer/unmunged_request_params.rb
|
197
240
|
- lib/api_hammer/version.rb
|
198
241
|
- lib/api_hammer/weblink.rb
|
242
|
+
- test/active_record_cache_find_by_test.rb
|
199
243
|
- test/check_required_params_test.rb
|
200
244
|
- test/halt_test.rb
|
201
245
|
- test/helper.rb
|
@@ -229,6 +273,7 @@ signing_key:
|
|
229
273
|
specification_version: 4
|
230
274
|
summary: an API tool
|
231
275
|
test_files:
|
276
|
+
- test/active_record_cache_find_by_test.rb
|
232
277
|
- test/check_required_params_test.rb
|
233
278
|
- test/halt_test.rb
|
234
279
|
- test/helper.rb
|