db_memoize 0.2.3 → 0.3.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.rubocop.yml +7 -15
- data/.travis.yml +4 -2
- data/README.md +2 -0
- data/Rakefile +13 -4
- data/VERSION +1 -0
- data/bin/rake +29 -0
- data/bin/rspec +29 -0
- data/bin/rubocop +29 -0
- data/db_memoize.gemspec +8 -14
- data/lib/db_memoize.rb +0 -1
- data/lib/db_memoize/helpers.rb +0 -12
- data/lib/db_memoize/migrations.rb +21 -1
- data/lib/db_memoize/model.rb +27 -36
- data/lib/db_memoize/value.rb +57 -2
- data/lib/db_memoize/version.rb +22 -1
- data/scripts/release +2 -0
- data/scripts/release.rb +93 -0
- metadata +36 -95
- data/lib/db_memoize/metal.rb +0 -127
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: '095aec4c4352df19e1a55ee270414a18b240e6135733e5871d5f766dd6789077'
|
4
|
+
data.tar.gz: d9ee2695e76c0611f23aa3cfe7c8afa82ff09f04d5de1acf525d2570099b2428
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 50af7b39e3aed56fa829b940f4c0251c0d49fef89306a38f7f6aa69ab7c6dfecb677646869eb076c155f9fbc69abc9a6bba3d37ea57d4582b0a0465433d90a11
|
7
|
+
data.tar.gz: 97e18054c9914f961c97796650f47fff3aca4e74066f6dbe53b420633719bb7ec0f3f8354c7d2e19c11e93b864192894e538204b9bf57626fbeda427734af19c
|
data/.rubocop.yml
CHANGED
@@ -3,21 +3,13 @@ AllCops:
|
|
3
3
|
Include:
|
4
4
|
- Rakefile
|
5
5
|
- config.ru
|
6
|
+
- lib/**/*.rb
|
6
7
|
Exclude:
|
7
8
|
- Rakefile
|
8
9
|
- bin/*
|
9
10
|
- config.ru
|
10
11
|
- config/**/*
|
11
|
-
- db/**/*
|
12
|
-
- doc/**/*
|
13
|
-
- gems/**/*
|
14
|
-
- gems/*/vendor/**/*
|
15
|
-
- script/**/*
|
16
|
-
- sh/**/*
|
17
|
-
- spec/rails_helper.rb
|
18
|
-
- spec/spec_helper.rb
|
19
12
|
- vendor/**/*
|
20
|
-
- spec/support/v20140601/requests_helper.rb
|
21
13
|
|
22
14
|
Style/FrozenStringLiteralComment:
|
23
15
|
Enabled: false
|
@@ -25,12 +17,6 @@ Style/FrozenStringLiteralComment:
|
|
25
17
|
Metrics/LineLength:
|
26
18
|
Max: 140
|
27
19
|
|
28
|
-
Rails:
|
29
|
-
Enabled: true
|
30
|
-
|
31
|
-
Rails/HasAndBelongsToMany:
|
32
|
-
Enabled: false
|
33
|
-
|
34
20
|
Style/Documentation:
|
35
21
|
Enabled: false
|
36
22
|
|
@@ -66,3 +52,9 @@ Metrics/MethodLength:
|
|
66
52
|
# "Prefer !expression.nil? over expression != nil." no way!
|
67
53
|
Style/NonNilCheck:
|
68
54
|
Enabled: false
|
55
|
+
|
56
|
+
Style/SymbolArray:
|
57
|
+
Enabled: false
|
58
|
+
|
59
|
+
Lint/MissingCopEnableDirective:
|
60
|
+
Enabled: false
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
data/Rakefile
CHANGED
@@ -1,11 +1,20 @@
|
|
1
|
-
require "bundler/gem_tasks"
|
2
1
|
require "rspec/core/rake_task"
|
3
2
|
|
4
3
|
RSpec::Core::RakeTask.new(:spec)
|
5
4
|
|
6
|
-
task
|
7
|
-
Rake.sh
|
8
|
-
Rake.sh
|
5
|
+
task 'db:test:create' do
|
6
|
+
Rake.sh 'dropdb db_memoize_test -U postgres || true'
|
7
|
+
Rake.sh 'createdb db_memoize_test -U postgres'
|
9
8
|
end
|
10
9
|
|
11
10
|
task :default => %w(db:test:create spec)
|
11
|
+
|
12
|
+
desc "release a new development gem version"
|
13
|
+
task :release do
|
14
|
+
sh "scripts/release.rb"
|
15
|
+
end
|
16
|
+
|
17
|
+
desc "release a new stable gem version"
|
18
|
+
task "release:stable" do
|
19
|
+
sh "BRANCH=stable scripts/release.rb"
|
20
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.3.2
|
data/bin/rake
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'rake' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require "pathname"
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
13
|
+
Pathname.new(__FILE__).realpath)
|
14
|
+
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
16
|
+
|
17
|
+
if File.file?(bundle_binstub)
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
19
|
+
load(bundle_binstub)
|
20
|
+
else
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require "rubygems"
|
27
|
+
require "bundler/setup"
|
28
|
+
|
29
|
+
load Gem.bin_path("rake", "rake")
|
data/bin/rspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'rspec' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require "pathname"
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
13
|
+
Pathname.new(__FILE__).realpath)
|
14
|
+
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
16
|
+
|
17
|
+
if File.file?(bundle_binstub)
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
19
|
+
load(bundle_binstub)
|
20
|
+
else
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require "rubygems"
|
27
|
+
require "bundler/setup"
|
28
|
+
|
29
|
+
load Gem.bin_path("rspec-core", "rspec")
|
data/bin/rubocop
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'rubocop' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require "pathname"
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
13
|
+
Pathname.new(__FILE__).realpath)
|
14
|
+
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
16
|
+
|
17
|
+
if File.file?(bundle_binstub)
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
19
|
+
load(bundle_binstub)
|
20
|
+
else
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require "rubygems"
|
27
|
+
require "bundler/setup"
|
28
|
+
|
29
|
+
load Gem.bin_path("rubocop", "rubocop")
|
data/db_memoize.gemspec
CHANGED
@@ -1,11 +1,10 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
lib = File.expand_path('../lib', __FILE__)
|
3
3
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require 'db_memoize/version'
|
5
4
|
|
6
5
|
Gem::Specification.new do |spec|
|
7
6
|
spec.name = "db_memoize"
|
8
|
-
spec.version =
|
7
|
+
spec.version = File.read("VERSION")
|
9
8
|
spec.licenses = ['MIT']
|
10
9
|
spec.authors = ["johannes-kostas goetzinger"]
|
11
10
|
spec.email = ["goetzinger@mediapeers.com"]
|
@@ -21,20 +20,15 @@ Gem::Specification.new do |spec|
|
|
21
20
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
22
21
|
spec.require_paths = ['lib']
|
23
22
|
|
23
|
+
spec.add_dependency 'simple-sql', '>= 0.4.20', '~> 0'
|
24
24
|
spec.add_development_dependency 'railties', '~> 4.2'
|
25
|
-
spec.add_development_dependency 'activerecord', '~> 4.2'
|
25
|
+
spec.add_development_dependency 'activerecord', '~> 4.2.10'
|
26
26
|
|
27
|
-
spec.add_development_dependency 'rake', '~>
|
28
|
-
spec.add_development_dependency 'pg'
|
29
|
-
spec.add_development_dependency 'rspec
|
30
|
-
spec.add_development_dependency '
|
31
|
-
spec.add_development_dependency '
|
32
|
-
spec.add_development_dependency 'yard', '~> 0'
|
33
|
-
spec.add_development_dependency 'simplecov', '~> 0.7.1'
|
34
|
-
spec.add_development_dependency 'rubocop', '~> 0.37.2'
|
27
|
+
spec.add_development_dependency 'rake', '~> 12.0'
|
28
|
+
spec.add_development_dependency 'pg', '~> 0.20'
|
29
|
+
spec.add_development_dependency 'rspec', '~> 3.8'
|
30
|
+
spec.add_development_dependency 'simplecov', '~> 0.16'
|
31
|
+
spec.add_development_dependency 'rubocop', '~> 0.59.2'
|
35
32
|
spec.add_development_dependency 'database_cleaner', '~> 1.5.3'
|
36
33
|
spec.add_development_dependency 'factory_girl', '~> 4.7.0'
|
37
|
-
spec.add_development_dependency 'ruby-progressbar', '~> 1.7'
|
38
|
-
spec.add_development_dependency 'awesome_print'
|
39
|
-
spec.add_development_dependency 'gem-release'
|
40
34
|
end
|
data/lib/db_memoize.rb
CHANGED
data/lib/db_memoize/helpers.rb
CHANGED
@@ -16,17 +16,5 @@ module DbMemoize
|
|
16
16
|
"DbMemoize <#{model.class.name}##{model.id}>##{method_name} - #{msg}"
|
17
17
|
end
|
18
18
|
end
|
19
|
-
|
20
|
-
def calculate_arguments_hash(arguments)
|
21
|
-
arguments.empty? ? nil : ::Digest::MD5.hexdigest(Marshal.dump(arguments))
|
22
|
-
end
|
23
|
-
|
24
|
-
def marshal(value)
|
25
|
-
Marshal.dump(value)
|
26
|
-
end
|
27
|
-
|
28
|
-
def unmarshal(value)
|
29
|
-
Marshal.load(value)
|
30
|
-
end
|
31
19
|
end
|
32
20
|
end
|
@@ -13,6 +13,8 @@ module DbMemoize
|
|
13
13
|
|
14
14
|
migration.add_index :memoized_values, [:entity_table_name, :entity_id]
|
15
15
|
migrate_empty_arguments_support(migration)
|
16
|
+
drop_arguments_support(migration)
|
17
|
+
add_discrete_value_columns(migration)
|
16
18
|
end
|
17
19
|
|
18
20
|
def migrate_empty_arguments_support(migration)
|
@@ -23,7 +25,25 @@ module DbMemoize
|
|
23
25
|
|
24
26
|
# add an index to be useful to look up entries where arguments_hash is NULL.
|
25
27
|
# (which is the case for plain attributes of an object)
|
26
|
-
migration.execute 'CREATE INDEX memoized_attributes_idx ON memoized_values((arguments_hash IS NULL))'
|
28
|
+
migration.execute 'CREATE INDEX IF NOT EXISTS memoized_attributes_idx ON memoized_values((arguments_hash IS NULL))'
|
29
|
+
end
|
30
|
+
|
31
|
+
def drop_arguments_support(migration)
|
32
|
+
migration.execute 'DROP INDEX IF EXISTS memoized_attributes_idx'
|
33
|
+
migration.execute 'ALTER TABLE memoized_values DROP COLUMN IF EXISTS arguments_hash'
|
34
|
+
|
35
|
+
migration.remove_index :memoized_values, [:entity_id, :entity_table_name]
|
36
|
+
migration.add_index :memoized_values, [:entity_id, :entity_table_name, :method_name], unique: true, name: 'memoized_attributes_idx2'
|
37
|
+
end
|
38
|
+
|
39
|
+
def add_discrete_value_columns(migration)
|
40
|
+
migration.execute 'ALTER TABLE memoized_values ADD COLUMN IF NOT EXISTS val_string varchar'
|
41
|
+
migration.execute 'ALTER TABLE memoized_values ADD COLUMN IF NOT EXISTS val_integer bigint'
|
42
|
+
migration.execute 'ALTER TABLE memoized_values ADD COLUMN IF NOT EXISTS val_float double precision'
|
43
|
+
migration.execute 'ALTER TABLE memoized_values ADD COLUMN IF NOT EXISTS val_time timestamp without time zone'
|
44
|
+
migration.execute 'ALTER TABLE memoized_values ADD COLUMN IF NOT EXISTS val_boolean boolean'
|
45
|
+
migration.execute 'ALTER TABLE memoized_values ADD COLUMN IF NOT EXISTS val_object jsonb'
|
46
|
+
migration.execute 'ALTER TABLE memoized_values ADD COLUMN IF NOT EXISTS val_nil boolean'
|
27
47
|
end
|
28
48
|
end
|
29
49
|
end
|
data/lib/db_memoize/model.rb
CHANGED
@@ -1,23 +1,23 @@
|
|
1
|
+
require 'simple-sql'
|
2
|
+
|
1
3
|
module DbMemoize
|
2
4
|
module Model
|
3
5
|
extend ActiveSupport::Concern
|
4
6
|
|
5
|
-
def memoized_value(method_name
|
6
|
-
|
7
|
-
|
8
|
-
end
|
7
|
+
def memoized_value(method_name)
|
8
|
+
memoizable = !changed? && persisted?
|
9
|
+
return send("#{method_name}_without_memoize") unless memoizable
|
9
10
|
|
10
11
|
value = nil
|
11
|
-
|
12
|
-
cached_value = find_memoized_value(method_name, args_hash)
|
12
|
+
cached_value = find_memoized_value(method_name)
|
13
13
|
|
14
14
|
if cached_value
|
15
|
-
value =
|
15
|
+
value = cached_value.value
|
16
16
|
Helpers.log(self, method_name, 'cache hit')
|
17
17
|
else
|
18
18
|
time = ::Benchmark.realtime do
|
19
|
-
value = send("#{method_name}_without_memoize"
|
20
|
-
create_memoized_value(method_name,
|
19
|
+
value = send("#{method_name}_without_memoize")
|
20
|
+
create_memoized_value(method_name, value)
|
21
21
|
end
|
22
22
|
Helpers.log(self, method_name, "cache miss. took #{Kernel.format '%.2f msecs', time * 1_000}")
|
23
23
|
end
|
@@ -37,33 +37,28 @@ module DbMemoize
|
|
37
37
|
# product.memoize_values full_title: "my full title",
|
38
38
|
# autocomplete_info: "my autocomplete_info"
|
39
39
|
#
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
40
|
+
# This sets the "full_title" and "autocomplete_info" values of the product.
|
41
|
+
#
|
42
|
+
def memoize_values(values)
|
44
43
|
values.each do |name, value|
|
45
|
-
create_memoized_value(name,
|
44
|
+
create_memoized_value(name, value)
|
46
45
|
end
|
47
46
|
end
|
48
47
|
|
49
48
|
private
|
50
49
|
|
51
|
-
def create_memoized_value(method_name,
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
value: Helpers.marshal(value)
|
57
|
-
|
58
|
-
@association_cache.delete :memoized_values
|
50
|
+
def create_memoized_value(method_name, value)
|
51
|
+
self.class.transaction do
|
52
|
+
::DbMemoize::Value.fast_create self.class.table_name, id, method_name, value
|
53
|
+
@association_cache.delete :memoized_values
|
54
|
+
end
|
59
55
|
end
|
60
56
|
|
61
|
-
def find_memoized_value(method_name
|
57
|
+
def find_memoized_value(method_name)
|
62
58
|
method_name = method_name.to_s
|
63
59
|
|
64
60
|
memoized_values.detect do |rec|
|
65
|
-
rec.method_name == method_name
|
66
|
-
rec.arguments_hash == args_hash
|
61
|
+
rec.method_name == method_name
|
67
62
|
end
|
68
63
|
end
|
69
64
|
|
@@ -93,19 +88,13 @@ module DbMemoize
|
|
93
88
|
DbMemoize::Value.where(conditions).delete_all_ordered
|
94
89
|
end
|
95
90
|
|
96
|
-
def memoize_values(records_or_ids, values
|
97
|
-
# [TODO] - when creating many memoized values: should we even support arguments here?
|
91
|
+
def memoize_values(records_or_ids, values)
|
98
92
|
transaction do
|
99
|
-
ids
|
100
|
-
arguments_hash = Helpers.calculate_arguments_hash(args)
|
93
|
+
ids = Helpers.find_ids(records_or_ids)
|
101
94
|
|
102
95
|
ids.each do |id|
|
103
96
|
values.each do |method_name, value|
|
104
|
-
::DbMemoize::Value.
|
105
|
-
entity_id: id,
|
106
|
-
method_name: method_name.to_s,
|
107
|
-
arguments_hash: arguments_hash,
|
108
|
-
value: Helpers.marshal(value)
|
97
|
+
::DbMemoize::Value.fast_create table_name, id, method_name, value
|
109
98
|
end
|
110
99
|
end
|
111
100
|
end
|
@@ -113,14 +102,16 @@ module DbMemoize
|
|
113
102
|
|
114
103
|
private
|
115
104
|
|
105
|
+
# rubocop:disable Style/EmptyBlockParameter
|
116
106
|
def create_memoized_alias_method(method_name)
|
117
|
-
define_method "#{method_name}_with_memoize" do
|
118
|
-
memoized_value(method_name
|
107
|
+
define_method "#{method_name}_with_memoize" do ||
|
108
|
+
memoized_value(method_name)
|
119
109
|
end
|
120
110
|
|
121
111
|
alias_method_chain method_name, :memoize
|
122
112
|
end
|
123
113
|
|
114
|
+
# rubocop:disable Style/GuardClause
|
124
115
|
def create_memoized_values_association
|
125
116
|
unless reflect_on_association(:memoized_values)
|
126
117
|
conditions = { entity_table_name: table_name }
|
data/lib/db_memoize/value.rb
CHANGED
@@ -1,15 +1,39 @@
|
|
1
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
2
|
+
|
3
|
+
require 'simple-sql'
|
4
|
+
require 'json'
|
5
|
+
|
1
6
|
module DbMemoize
|
2
7
|
class Value < ActiveRecord::Base
|
3
8
|
self.table_name = 'memoized_values'
|
4
9
|
|
5
|
-
|
10
|
+
SQL = ::Simple::SQL
|
11
|
+
|
12
|
+
def value=(value)
|
13
|
+
self.val_integer = self.val_float = self.val_string = self.val_boolean = self.val_time = nil
|
14
|
+
|
15
|
+
case value
|
16
|
+
when String then self.val_string = value
|
17
|
+
when Integer then self.val_integer = value
|
18
|
+
when Float then self.val_float = value
|
19
|
+
when Time then self.val_time = value
|
20
|
+
when false then self.val_boolean = value
|
21
|
+
when true then self.val_boolean = value
|
22
|
+
when nil then :nop
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def value
|
27
|
+
# Note: val_boolean should come last.
|
28
|
+
val_string || val_integer || val_float || val_time || val_object || val_boolean
|
29
|
+
end
|
6
30
|
|
7
31
|
def self.delete_all_ordered
|
8
32
|
relation = self
|
9
33
|
relation = all unless is_a?(ActiveRecord::Relation)
|
10
34
|
|
11
35
|
sql = relation.select(:ctid).to_sql
|
12
|
-
|
36
|
+
SQL.ask <<-SQL
|
13
37
|
DO $$DECLARE c record;
|
14
38
|
BEGIN
|
15
39
|
FOR c IN #{sql} ORDER BY ctid LOOP
|
@@ -18,5 +42,36 @@ module DbMemoize
|
|
18
42
|
END$$;
|
19
43
|
SQL
|
20
44
|
end
|
45
|
+
|
46
|
+
def self.fast_create(entity_table_name, id, method_name, value)
|
47
|
+
method_name = method_name.to_s
|
48
|
+
|
49
|
+
# clear out old entry (if any). This makes sure that any existing value
|
50
|
+
# is cleared out properly.
|
51
|
+
SQL.ask "DELETE FROM #{table_name} WHERE(entity_table_name, entity_id, method_name) = ($1, $2, $3)",
|
52
|
+
entity_table_name, id, method_name
|
53
|
+
|
54
|
+
dest_column = case value
|
55
|
+
when String then :val_string
|
56
|
+
when Integer then :val_integer
|
57
|
+
when Float then :val_float
|
58
|
+
when Time then :val_time
|
59
|
+
when false then :val_boolean
|
60
|
+
when nil then :val_nil
|
61
|
+
when Hash then :val_object
|
62
|
+
when Array then :val_object
|
63
|
+
else
|
64
|
+
raise "Unsupported value of type #{value.class.name}: #{value.inspect}"
|
65
|
+
end
|
66
|
+
|
67
|
+
value = JSON.generate(value) if dest_column == :val_object
|
68
|
+
sql = <<~SQL.freeze
|
69
|
+
INSERT INTO #{table_name}
|
70
|
+
(entity_table_name, entity_id, method_name, #{dest_column}, created_at)
|
71
|
+
VALUES($1,$2,$3,$4,NOW())
|
72
|
+
SQL
|
73
|
+
|
74
|
+
SQL.ask sql, entity_table_name, id, method_name, value
|
75
|
+
end
|
21
76
|
end
|
22
77
|
end
|
data/lib/db_memoize/version.rb
CHANGED
@@ -1,3 +1,24 @@
|
|
1
1
|
module DbMemoize
|
2
|
-
|
2
|
+
module GemHelper
|
3
|
+
module_function
|
4
|
+
|
5
|
+
def version(name)
|
6
|
+
spec = Gem.loaded_specs[name]
|
7
|
+
version = spec.version.to_s
|
8
|
+
version += '+unreleased' if unreleased?(spec)
|
9
|
+
version
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def unreleased?(spec)
|
15
|
+
return false unless defined?(Bundler::Source::Gemspec)
|
16
|
+
return true if spec.source.is_a?(::Bundler::Source::Gemspec)
|
17
|
+
return true if spec.source.is_a?(::Bundler::Source::Path)
|
18
|
+
|
19
|
+
false
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
VERSION = GemHelper.version 'db_memoize'
|
3
24
|
end
|
data/scripts/release
ADDED
data/scripts/release.rb
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# -- helpers ------------------------------------------------------------------
|
4
|
+
|
5
|
+
def sys(cmd)
|
6
|
+
STDERR.puts "> #{cmd}"
|
7
|
+
system cmd
|
8
|
+
return true if $?.success?
|
9
|
+
|
10
|
+
STDERR.puts "> #{cmd} returned with exitstatus #{$?.exitstatus}"
|
11
|
+
$?.success?
|
12
|
+
end
|
13
|
+
|
14
|
+
def sys!(cmd, error: nil)
|
15
|
+
return true if sys(cmd)
|
16
|
+
STDERR.puts error if error
|
17
|
+
exit 1
|
18
|
+
end
|
19
|
+
|
20
|
+
def die!(msg)
|
21
|
+
STDERR.puts msg
|
22
|
+
exit 1
|
23
|
+
end
|
24
|
+
|
25
|
+
ROOT = File.expand_path("#{File.dirname(__FILE__)}/..")
|
26
|
+
|
27
|
+
GEMSPEC = Dir.glob("*.gemspec").first || die!("Missing gemspec file.")
|
28
|
+
|
29
|
+
# -- Version reading and bumping ----------------------------------------------
|
30
|
+
|
31
|
+
module Version
|
32
|
+
extend self
|
33
|
+
|
34
|
+
VERSION_FILE = "#{Dir.getwd}/VERSION"
|
35
|
+
|
36
|
+
def read_version
|
37
|
+
version = File.exist?(VERSION_FILE) ? File.read(VERSION_FILE) : "0.0.1"
|
38
|
+
version.chomp!
|
39
|
+
raise "Invalid version number in #{VERSION_FILE}" unless version =~ /^\d+\.\d+\.\d+$/
|
40
|
+
version
|
41
|
+
end
|
42
|
+
|
43
|
+
def auto_version_bump
|
44
|
+
old_version_number = read_version
|
45
|
+
old = old_version_number.split('.')
|
46
|
+
|
47
|
+
current = old[0..-2] << old[-1].next
|
48
|
+
current.join('.')
|
49
|
+
end
|
50
|
+
|
51
|
+
def bump_version
|
52
|
+
next_version = ENV["VERSION"] || auto_version_bump
|
53
|
+
File.open(VERSION_FILE, "w") { |io| io.write next_version }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# -- check, bump, release a new gem version -----------------------------------
|
58
|
+
|
59
|
+
Dir.chdir ROOT
|
60
|
+
$BASE_BRANCH = ENV['BRANCH'] || 'master'
|
61
|
+
|
62
|
+
# ENV["BUNDLE_GEMFILE"] = "#{Dir.getwd}/Gemfile"
|
63
|
+
# sys! "bundle install"
|
64
|
+
|
65
|
+
sys! "git diff --exit-code > /dev/null", error: 'There are unstaged changes in your working directory'
|
66
|
+
sys! "git diff --cached --exit-code > /dev/null", error: 'There are staged but uncommitted changes'
|
67
|
+
|
68
|
+
sys! "git checkout #{$BASE_BRANCH}"
|
69
|
+
sys! "git pull"
|
70
|
+
|
71
|
+
Version.bump_version
|
72
|
+
version = Version.read_version
|
73
|
+
|
74
|
+
sys! "git add VERSION"
|
75
|
+
sys! "git commit -m \"bump gem to v#{version}\""
|
76
|
+
sys! "git tag -a v#{version} -m \"Tag #{version}\""
|
77
|
+
|
78
|
+
sys! "gem build #{GEMSPEC}"
|
79
|
+
|
80
|
+
sys! "git push origin #{$BASE_BRANCH}"
|
81
|
+
sys! 'git push --tags --force'
|
82
|
+
|
83
|
+
# sys! "bundle exec fury push #{Dir.glob('*.gem').first} --as mediapeers"
|
84
|
+
sys! "gem push #{Dir.glob('*.gem').first}"
|
85
|
+
|
86
|
+
sys! "mkdir -p pkg"
|
87
|
+
sys! "mv *.gem pkg"
|
88
|
+
|
89
|
+
STDERR.puts <<-MSG
|
90
|
+
================================================================================
|
91
|
+
Thank you for releasing a new gem version. You made my day.
|
92
|
+
================================================================================
|
93
|
+
MSG
|
metadata
CHANGED
@@ -1,31 +1,37 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: db_memoize
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2
|
4
|
+
version: 0.3.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- johannes-kostas goetzinger
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-10-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: simple-sql
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.4.20
|
17
20
|
- - "~>"
|
18
21
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
20
|
-
type: :
|
22
|
+
version: '0'
|
23
|
+
type: :runtime
|
21
24
|
prerelease: false
|
22
25
|
version_requirements: !ruby/object:Gem::Requirement
|
23
26
|
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 0.4.20
|
24
30
|
- - "~>"
|
25
31
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
32
|
+
version: '0'
|
27
33
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
34
|
+
name: railties
|
29
35
|
requirement: !ruby/object:Gem::Requirement
|
30
36
|
requirements:
|
31
37
|
- - "~>"
|
@@ -39,117 +45,89 @@ dependencies:
|
|
39
45
|
- !ruby/object:Gem::Version
|
40
46
|
version: '4.2'
|
41
47
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - "~>"
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '10.5'
|
48
|
-
type: :development
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - "~>"
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '10.5'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: pg
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - ">="
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: '0'
|
62
|
-
type: :development
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - ">="
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: '0'
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: rspec-rails
|
48
|
+
name: activerecord
|
71
49
|
requirement: !ruby/object:Gem::Requirement
|
72
50
|
requirements:
|
73
51
|
- - "~>"
|
74
52
|
- !ruby/object:Gem::Version
|
75
|
-
version:
|
53
|
+
version: 4.2.10
|
76
54
|
type: :development
|
77
55
|
prerelease: false
|
78
56
|
version_requirements: !ruby/object:Gem::Requirement
|
79
57
|
requirements:
|
80
58
|
- - "~>"
|
81
59
|
- !ruby/object:Gem::Version
|
82
|
-
version:
|
60
|
+
version: 4.2.10
|
83
61
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
62
|
+
name: rake
|
85
63
|
requirement: !ruby/object:Gem::Requirement
|
86
64
|
requirements:
|
87
65
|
- - "~>"
|
88
66
|
- !ruby/object:Gem::Version
|
89
|
-
version: '0
|
67
|
+
version: '12.0'
|
90
68
|
type: :development
|
91
69
|
prerelease: false
|
92
70
|
version_requirements: !ruby/object:Gem::Requirement
|
93
71
|
requirements:
|
94
72
|
- - "~>"
|
95
73
|
- !ruby/object:Gem::Version
|
96
|
-
version: '0
|
74
|
+
version: '12.0'
|
97
75
|
- !ruby/object:Gem::Dependency
|
98
|
-
name:
|
76
|
+
name: pg
|
99
77
|
requirement: !ruby/object:Gem::Requirement
|
100
78
|
requirements:
|
101
79
|
- - "~>"
|
102
80
|
- !ruby/object:Gem::Version
|
103
|
-
version: '
|
81
|
+
version: '0.20'
|
104
82
|
type: :development
|
105
83
|
prerelease: false
|
106
84
|
version_requirements: !ruby/object:Gem::Requirement
|
107
85
|
requirements:
|
108
86
|
- - "~>"
|
109
87
|
- !ruby/object:Gem::Version
|
110
|
-
version: '
|
88
|
+
version: '0.20'
|
111
89
|
- !ruby/object:Gem::Dependency
|
112
|
-
name:
|
90
|
+
name: rspec
|
113
91
|
requirement: !ruby/object:Gem::Requirement
|
114
92
|
requirements:
|
115
93
|
- - "~>"
|
116
94
|
- !ruby/object:Gem::Version
|
117
|
-
version: '
|
95
|
+
version: '3.8'
|
118
96
|
type: :development
|
119
97
|
prerelease: false
|
120
98
|
version_requirements: !ruby/object:Gem::Requirement
|
121
99
|
requirements:
|
122
100
|
- - "~>"
|
123
101
|
- !ruby/object:Gem::Version
|
124
|
-
version: '
|
102
|
+
version: '3.8'
|
125
103
|
- !ruby/object:Gem::Dependency
|
126
104
|
name: simplecov
|
127
105
|
requirement: !ruby/object:Gem::Requirement
|
128
106
|
requirements:
|
129
107
|
- - "~>"
|
130
108
|
- !ruby/object:Gem::Version
|
131
|
-
version: 0.
|
109
|
+
version: '0.16'
|
132
110
|
type: :development
|
133
111
|
prerelease: false
|
134
112
|
version_requirements: !ruby/object:Gem::Requirement
|
135
113
|
requirements:
|
136
114
|
- - "~>"
|
137
115
|
- !ruby/object:Gem::Version
|
138
|
-
version: 0.
|
116
|
+
version: '0.16'
|
139
117
|
- !ruby/object:Gem::Dependency
|
140
118
|
name: rubocop
|
141
119
|
requirement: !ruby/object:Gem::Requirement
|
142
120
|
requirements:
|
143
121
|
- - "~>"
|
144
122
|
- !ruby/object:Gem::Version
|
145
|
-
version: 0.
|
123
|
+
version: 0.59.2
|
146
124
|
type: :development
|
147
125
|
prerelease: false
|
148
126
|
version_requirements: !ruby/object:Gem::Requirement
|
149
127
|
requirements:
|
150
128
|
- - "~>"
|
151
129
|
- !ruby/object:Gem::Version
|
152
|
-
version: 0.
|
130
|
+
version: 0.59.2
|
153
131
|
- !ruby/object:Gem::Dependency
|
154
132
|
name: database_cleaner
|
155
133
|
requirement: !ruby/object:Gem::Requirement
|
@@ -178,48 +156,6 @@ dependencies:
|
|
178
156
|
- - "~>"
|
179
157
|
- !ruby/object:Gem::Version
|
180
158
|
version: 4.7.0
|
181
|
-
- !ruby/object:Gem::Dependency
|
182
|
-
name: ruby-progressbar
|
183
|
-
requirement: !ruby/object:Gem::Requirement
|
184
|
-
requirements:
|
185
|
-
- - "~>"
|
186
|
-
- !ruby/object:Gem::Version
|
187
|
-
version: '1.7'
|
188
|
-
type: :development
|
189
|
-
prerelease: false
|
190
|
-
version_requirements: !ruby/object:Gem::Requirement
|
191
|
-
requirements:
|
192
|
-
- - "~>"
|
193
|
-
- !ruby/object:Gem::Version
|
194
|
-
version: '1.7'
|
195
|
-
- !ruby/object:Gem::Dependency
|
196
|
-
name: awesome_print
|
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'
|
209
|
-
- !ruby/object:Gem::Dependency
|
210
|
-
name: gem-release
|
211
|
-
requirement: !ruby/object:Gem::Requirement
|
212
|
-
requirements:
|
213
|
-
- - ">="
|
214
|
-
- !ruby/object:Gem::Version
|
215
|
-
version: '0'
|
216
|
-
type: :development
|
217
|
-
prerelease: false
|
218
|
-
version_requirements: !ruby/object:Gem::Requirement
|
219
|
-
requirements:
|
220
|
-
- - ">="
|
221
|
-
- !ruby/object:Gem::Version
|
222
|
-
version: '0'
|
223
159
|
description: library to cache (memoize) method return values in database
|
224
160
|
email:
|
225
161
|
- goetzinger@mediapeers.com
|
@@ -235,12 +171,15 @@ files:
|
|
235
171
|
- LICENSE
|
236
172
|
- README.md
|
237
173
|
- Rakefile
|
174
|
+
- VERSION
|
238
175
|
- bin/console
|
176
|
+
- bin/rake
|
177
|
+
- bin/rspec
|
178
|
+
- bin/rubocop
|
239
179
|
- bin/setup
|
240
180
|
- db_memoize.gemspec
|
241
181
|
- lib/db_memoize.rb
|
242
182
|
- lib/db_memoize/helpers.rb
|
243
|
-
- lib/db_memoize/metal.rb
|
244
183
|
- lib/db_memoize/migrations.rb
|
245
184
|
- lib/db_memoize/model.rb
|
246
185
|
- lib/db_memoize/railtie.rb
|
@@ -249,6 +188,8 @@ files:
|
|
249
188
|
- lib/tasks/clear.rake
|
250
189
|
- lib/tasks/warmup.rake
|
251
190
|
- log/.keep
|
191
|
+
- scripts/release
|
192
|
+
- scripts/release.rb
|
252
193
|
homepage: https://github.com/mediapeers/db_memoize
|
253
194
|
licenses:
|
254
195
|
- MIT
|
@@ -269,7 +210,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
269
210
|
version: '0'
|
270
211
|
requirements: []
|
271
212
|
rubyforge_project:
|
272
|
-
rubygems_version: 2.
|
213
|
+
rubygems_version: 2.7.7
|
273
214
|
signing_key:
|
274
215
|
specification_version: 4
|
275
216
|
summary: library to cache (memoize) method return values in database
|
data/lib/db_memoize/metal.rb
DELETED
@@ -1,127 +0,0 @@
|
|
1
|
-
module DbMemoize
|
2
|
-
module Metal
|
3
|
-
def self.included(base)
|
4
|
-
base.extend ClassMethods
|
5
|
-
# base.metal # initialize the metal adapter
|
6
|
-
end
|
7
|
-
|
8
|
-
module ClassMethods
|
9
|
-
def metal
|
10
|
-
@metal ||= Adapter.new(self)
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
class Adapter
|
15
|
-
class PkInfo
|
16
|
-
attr_reader :column, :type
|
17
|
-
|
18
|
-
def initialize(base_klass)
|
19
|
-
@column = base_klass.primary_key
|
20
|
-
@type = @column && base_klass.columns_hash.fetch(@column).type
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def initialize(base_klass)
|
25
|
-
@base_klass = base_klass
|
26
|
-
@query_cache = {}
|
27
|
-
end
|
28
|
-
|
29
|
-
private
|
30
|
-
|
31
|
-
# setup primary key information. This is necessary to allow the create! method
|
32
|
-
# to return the primary key of a newly created entry.
|
33
|
-
def primary_key
|
34
|
-
@primary_key ||= PkInfo.new(@base_klass)
|
35
|
-
end
|
36
|
-
|
37
|
-
def table_name
|
38
|
-
@base_klass.table_name
|
39
|
-
end
|
40
|
-
|
41
|
-
def raw_connection
|
42
|
-
@base_klass.connection.raw_connection # do not memoize me!
|
43
|
-
end
|
44
|
-
|
45
|
-
def column?(column_name)
|
46
|
-
@base_klass.columns_hash.key?(column_name)
|
47
|
-
end
|
48
|
-
|
49
|
-
class Inserter
|
50
|
-
def initialize(sql:, bytea_indices:)
|
51
|
-
@sql = sql
|
52
|
-
@bytea_indices = bytea_indices
|
53
|
-
end
|
54
|
-
|
55
|
-
def exec(raw_connection:, values:)
|
56
|
-
@bytea_indices.each do |bytea_index|
|
57
|
-
value = values[bytea_index]
|
58
|
-
values[bytea_index] = PGconn.escape_bytea(value) if value
|
59
|
-
end
|
60
|
-
|
61
|
-
raw_connection.exec_params(@sql, values)
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
# returns an Inserter
|
66
|
-
def inserter(field_names)
|
67
|
-
@query_cache[field_names] ||= _inserter(field_names)
|
68
|
-
end
|
69
|
-
|
70
|
-
DATABASE_IDENTIFIER_REGEX = /\A\w+\z/
|
71
|
-
|
72
|
-
def check_database_identifiers!(*strings)
|
73
|
-
strings.each do |s|
|
74
|
-
next if DATABASE_IDENTIFIER_REGEX =~ s
|
75
|
-
raise ArgumentError, "Invalid database identifier: #{s.inspect}"
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
def _inserter(field_names)
|
80
|
-
check_database_identifiers! table_name, *field_names
|
81
|
-
|
82
|
-
placeholders = 0.upto(field_names.count - 1).map { |idx| "$#{idx + 1}" }
|
83
|
-
|
84
|
-
if column?('created_at')
|
85
|
-
field_names << 'created_at'
|
86
|
-
placeholders << 'current_timestamp'
|
87
|
-
end
|
88
|
-
|
89
|
-
if column?('updated_at')
|
90
|
-
field_names << 'updated_at'
|
91
|
-
placeholders << 'current_timestamp'
|
92
|
-
end
|
93
|
-
|
94
|
-
sql = "INSERT INTO #{table_name} (#{field_names.join(',')}) VALUES(#{placeholders.join(',')})"
|
95
|
-
sql += " RETURNING #{primary_key.column}" if primary_key.column
|
96
|
-
|
97
|
-
columns_hash = @base_klass.columns_hash
|
98
|
-
bytea_indices = []
|
99
|
-
field_names.each_with_index { |column, idx|
|
100
|
-
next unless :binary == columns_hash.fetch(column).type
|
101
|
-
bytea_indices << idx
|
102
|
-
}
|
103
|
-
|
104
|
-
Inserter.new sql: sql, bytea_indices: bytea_indices
|
105
|
-
end
|
106
|
-
|
107
|
-
public
|
108
|
-
|
109
|
-
def create!(record)
|
110
|
-
keys, values = record.to_a.transpose
|
111
|
-
keys = keys.map(&:to_s)
|
112
|
-
|
113
|
-
result = inserter(keys).exec(raw_connection: raw_connection, values: values)
|
114
|
-
|
115
|
-
# if we don't have an ID column then the sql does not return any value. The result
|
116
|
-
# object would be this: #<PG::Result status=PGRES_COMMAND_OK ntuples=0 nfields=0 cmd_tuples=1>
|
117
|
-
# we just return nil in that case; otherwise we return the first entry of the first result row
|
118
|
-
# which would be the stringified id.
|
119
|
-
first_row = result.each_row.first
|
120
|
-
return nil unless first_row
|
121
|
-
|
122
|
-
id = first_row.first
|
123
|
-
primary_key.type == :integer ? Integer(id) : id
|
124
|
-
end
|
125
|
-
end
|
126
|
-
end
|
127
|
-
end
|