kasket 4.4.3 → 4.4.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ba85f36144f05d60bbebcba8a11ff48b930d29cf
4
- data.tar.gz: a0ac4b0539ecf1c19f700ec08318a17ea5f01c6f
3
+ metadata.gz: 5e93590bf7fe78f25a1d988db077414a374e0a93
4
+ data.tar.gz: cd462545f88c3a6e1721b9be75fee8e1c6ec6905
5
5
  SHA512:
6
- metadata.gz: a25702c95434941d087060eb6c6cab9dd3843701efe293709cdd69de9c6a0ef2d11c9f194a69e9d4c4f703f3e3e1f2ede9970bb58cee42baa8db4b39aee43458
7
- data.tar.gz: c68f3966ba94bc372ce16d7e2fd8996ed009ff362f49355181cdf471764b387d373cf540dda854531bd568de2115978b37f54b75a3252ba73b74dce2af46b606
6
+ metadata.gz: de430ca6f3a4ee7c0846c7f398cd01592dd7f8dad5972822f9d201f075a11d7c4b6648ab5a2c54afdf2dd5de2a7b4370462f1b7bf78eb855cdb7528e9698f15a
7
+ data.tar.gz: bb2894c5baad7af7865cb206c7293eee9534d5acd4a20022f360c125aeb55d58fcf630c05828090d6b9c31bb0891f878003f212e5f609055dd0b727329c6b6c9
data/lib/kasket.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'active_record'
2
3
  require 'active_support'
3
4
 
@@ -14,7 +15,7 @@ module Kasket
14
15
  autoload :SelectManagerMixin, 'kasket/select_manager_mixin'
15
16
  autoload :RelationMixin, 'kasket/relation_mixin'
16
17
 
17
- CONFIGURATION = {:max_collection_size => 100, :write_through => false}
18
+ CONFIGURATION = { max_collection_size: 100, write_through: false } # rubocop:disable Style/MutableConstant
18
19
 
19
20
  module_function
20
21
 
@@ -1,11 +1,10 @@
1
+ # frozen_string_literal: true
1
2
  require 'active_support'
2
3
  require "digest/md5"
3
4
 
4
5
  module Kasket
5
-
6
6
  module ConfigurationMixin
7
-
8
- def without_kasket(&block)
7
+ def without_kasket
9
8
  old_value = Thread.current[:kasket_disabled] || false
10
9
  Thread.current[:kasket_disabled] = true
11
10
  yield
@@ -22,7 +21,10 @@ module Kasket
22
21
  end
23
22
 
24
23
  def kasket_key_prefix
25
- @kasket_key_prefix ||= "kasket-#{Kasket::Version::PROTOCOL}/#{kasket_activerecord_version}/#{table_name}/version=#{column_names.join.sum}/"
24
+ @kasket_key_prefix ||= begin
25
+ column_hash = column_names.join.sum
26
+ "kasket-#{Kasket::Version::PROTOCOL}/#{kasket_activerecord_version}/#{table_name}/version=#{column_hash}/"
27
+ end
26
28
  end
27
29
 
28
30
  def kasket_activerecord_version
@@ -36,7 +38,7 @@ module Kasket
36
38
  key = attribute_value_pairs.map do |attribute, value|
37
39
  column = columns_hash[attribute.to_s]
38
40
  value = nil if value.blank?
39
- attribute.to_s << '=' << quoted_value_for_column(value, column)
41
+ "#{attribute}=#{quoted_value_for_column(value, column)}"
40
42
  end.join('/')
41
43
 
42
44
  if key.size > (250 - kasket_key_prefix.size) || key =~ /\s/
@@ -65,12 +67,14 @@ module Kasket
65
67
  kasket_indices.include?(sorted_attributes)
66
68
  end
67
69
 
68
- def has_kasket(options = {})
70
+ def has_kasket
69
71
  has_kasket_on :id
70
72
  end
71
73
 
72
74
  def has_kasket_on(*args)
73
75
  attributes = args.sort_by!(&:to_s)
76
+
77
+ # enforce id index
74
78
  if attributes != [:id] && !has_kasket_index_on?([:id])
75
79
  has_kasket_on(:id)
76
80
  end
@@ -93,14 +97,14 @@ module Kasket
93
97
 
94
98
  def quoted_value_for_column(value, column)
95
99
  if column
96
- casted_value = case
97
- when connection.respond_to?(:type_cast_from_column)
98
- connection.type_cast_from_column(column, value)
99
- when column.respond_to?(:type_cast_for_database)
100
- column.type_cast_for_database(value) # Rails 4.2
101
- else
102
- column.type_cast(value)
103
- end
100
+ casted_value =
101
+ if connection.respond_to?(:type_cast_from_column)
102
+ connection.type_cast_from_column(column, value)
103
+ elsif column.respond_to?(:type_cast_for_database)
104
+ column.type_cast_for_database(value) # Rails 4.2
105
+ else
106
+ column.type_cast(value)
107
+ end
104
108
  connection.quote(casted_value).downcase
105
109
  else
106
110
  value.to_s
@@ -1,9 +1,10 @@
1
+ # frozen_string_literal: true
1
2
  module Kasket
2
3
  module DirtyMixin
3
4
  def kasket_dirty_methods(*method_names)
4
5
  method_names.each do |method|
5
6
  without = "without_kasket_update_#{method}"
6
- return if method_defined? without
7
+ break if method_defined? without
7
8
 
8
9
  alias_method without, method
9
10
  define_method method do |*args, &block|
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Kasket
2
3
  class QueryParser
3
4
  # Examples:
@@ -10,16 +11,21 @@ module Kasket
10
11
 
11
12
  def initialize(model_class)
12
13
  @model_class = model_class
14
+
13
15
  @supported_query_pattern = /^select\s+((?:`|")#{@model_class.table_name}(?:`|")\.)?\* from (?:`|")#{@model_class.table_name}(?:`|") where (.*?)(|\s+limit 1)\s*$/i
14
- @table_and_column_pattern = /(?:(?:`|")?#{@model_class.table_name}(?:`|")?\.)?(?:`|")?([a-zA-Z]\w*)(?:`|")?/ # Matches: `users`.id, `users`.`id`, users.id, id
15
- @key_eq_value_pattern = /^[\(\s]*#{@table_and_column_pattern}\s+(=|IN)\s+#{VALUE}[\)\s]*$/ # Matches: KEY = VALUE, (KEY = VALUE), ()(KEY = VALUE))
16
+
17
+ # Matches: `users`.id, `users`.`id`, users.id, id
18
+ @table_and_column_pattern = /(?:(?:`|")?#{@model_class.table_name}(?:`|")?\.)?(?:`|")?([a-zA-Z]\w*)(?:`|")?/
19
+ # Matches: KEY = VALUE, (KEY = VALUE), ()(KEY = VALUE))
20
+ @key_eq_value_pattern = /^[\(\s]*#{@table_and_column_pattern}\s+(=|IN)\s+#{VALUE}[\)\s]*$/
16
21
  end
17
22
 
18
23
  def parse(sql)
19
24
  if match = @supported_query_pattern.match(sql)
20
- where, limit = match[2], match[3]
25
+ where = match[2]
26
+ limit = match[3]
21
27
 
22
- query = Hash.new
28
+ query = {}
23
29
  query[:attributes] = sorted_attribute_value_pairs(where)
24
30
  return nil if query[:attributes].nil?
25
31
 
@@ -38,33 +44,32 @@ module Kasket
38
44
 
39
45
  private
40
46
 
41
- def sorted_attribute_value_pairs(conditions)
42
- if attributes = parse_condition(conditions)
43
- attributes.sort { |pair1, pair2| pair1[0].to_s <=> pair2[0].to_s }
44
- end
47
+ def sorted_attribute_value_pairs(conditions)
48
+ if attributes = parse_condition(conditions)
49
+ attributes.sort { |pair1, pair2| pair1[0].to_s <=> pair2[0].to_s }
45
50
  end
51
+ end
46
52
 
47
- def parse_condition(conditions = '', *values)
48
- values = values.dup
49
- conditions.split(AND).inject([]) do |pairs, condition|
50
- matched, column_name, operator, sql_value = *(@key_eq_value_pattern.match(condition))
51
- if matched
52
- if operator == 'IN'
53
- if column_name == 'id'
54
- values = sql_value[1..(-2)].split(',').map(&:strip)
55
- pairs << [column_name.to_sym, values]
56
- else
57
- return nil
58
- end
53
+ def parse_condition(conditions = '', *values)
54
+ values = values.dup
55
+ conditions.split(AND).inject([]) do |pairs, condition|
56
+ matched, column_name, operator, sql_value = *@key_eq_value_pattern.match(condition)
57
+ if matched
58
+ if operator == 'IN'
59
+ if column_name == 'id'
60
+ values = sql_value[1..-2].split(',').map(&:strip)
61
+ pairs << [column_name.to_sym, values]
59
62
  else
60
- value = sql_value == '?' ? values.shift : sql_value
61
- pairs << [column_name.to_sym, value.gsub(/''|\\'/, "'")]
63
+ return nil
62
64
  end
63
65
  else
64
- return nil
66
+ value = sql_value == '?' ? values.shift : sql_value
67
+ pairs << [column_name.to_sym, value.gsub(/''|\\'/, "'")]
65
68
  end
69
+ else
70
+ return nil
66
71
  end
67
72
  end
68
-
73
+ end
69
74
  end
70
75
  end
@@ -1,6 +1,6 @@
1
+ # frozen_string_literal: true
1
2
  module Kasket
2
3
  module ReadMixin
3
-
4
4
  def self.extended(base)
5
5
  class << base
6
6
  alias_method :find_by_sql_without_kasket, :find_by_sql
@@ -12,14 +12,14 @@ module Kasket
12
12
  sql = args[0]
13
13
 
14
14
  if use_kasket?
15
- if sql.respond_to?(:to_kasket_query)
15
+ query = if sql.respond_to?(:to_kasket_query)
16
16
  if ActiveRecord::VERSION::MAJOR < 5
17
- query = sql.to_kasket_query(self, args[1])
17
+ sql.to_kasket_query(self, args[1])
18
18
  else
19
- query = sql.to_kasket_query(self, args[1].map(&:value_for_database))
19
+ sql.to_kasket_query(self, args[1].map(&:value_for_database))
20
20
  end
21
21
  else
22
- query = kasket_parser.parse(sanitize_sql(sql))
22
+ kasket_parser.parse(sanitize_sql(sql))
23
23
  end
24
24
  end
25
25
 
@@ -45,8 +45,8 @@ module Kasket
45
45
  def find_by_sql_with_kasket_on_id_array(keys)
46
46
  key_attributes_map = Kasket.cache.read_multi(*keys)
47
47
 
48
- found_keys, missing_keys = keys.partition{|k| key_attributes_map[k] }
49
- found_keys.each{|k| key_attributes_map[k] = instantiate(key_attributes_map[k].dup) }
48
+ found_keys, missing_keys = keys.partition {|k| key_attributes_map[k] }
49
+ found_keys.each {|k| key_attributes_map[k] = instantiate(key_attributes_map[k].dup) }
50
50
  key_attributes_map.merge!(missing_records_from_db(missing_keys))
51
51
 
52
52
  key_attributes_map.values.compact
@@ -65,11 +65,11 @@ module Kasket
65
65
  def missing_records_from_db(missing_keys)
66
66
  return {} if missing_keys.empty?
67
67
 
68
- id_key_map = Hash[missing_keys.map{|key| [key.split('=').last.to_i, key] }]
68
+ id_key_map = Hash[missing_keys.map {|key| [key.split('=').last.to_i, key] }]
69
69
 
70
- found = without_kasket { where(:id => id_key_map.keys).to_a }
70
+ found = without_kasket { where(id: id_key_map.keys).to_a }
71
71
  found.each(&:store_in_kasket)
72
- Hash[found.map{|record| [id_key_map[record.id], record] }]
72
+ Hash[found.map {|record| [id_key_map[record.id], record] }]
73
73
  end
74
74
 
75
75
  def store_in_kasket(key, records)
@@ -1,12 +1,13 @@
1
+ # frozen_string_literal: true
1
2
  module Kasket
2
3
  module RelationMixin
3
4
  def to_kasket_query(binds = nil)
4
5
  if arel.is_a?(Arel::SelectManager)
5
- if ActiveRecord::VERSION::MAJOR < 5
6
- arel.to_kasket_query(klass, (binds || bind_values))
7
- else
8
- arel.to_kasket_query(klass, (@values[:where].binds.map(&:value_for_database) + Array(@values[:limit])))
9
- end
6
+ if ActiveRecord::VERSION::MAJOR < 5
7
+ arel.to_kasket_query(klass, (binds || bind_values))
8
+ else
9
+ arel.to_kasket_query(klass, (@values[:where].binds.map(&:value_for_database) + Array(@values[:limit])))
10
+ end
10
11
  end
11
12
  rescue TypeError # unsupported object in ast
12
13
  return nil
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Kasket
2
3
  module SelectManagerMixin
3
4
  def to_kasket_query(klass, binds = [])
@@ -17,7 +18,7 @@ module Kasket
17
18
  # return nil if !query[:index].include?(:id)
18
19
  end
19
20
 
20
- if query[:index].size > 1 && query[:attributes].any? { |attribute, value| value.is_a?(Array) }
21
+ if query[:index].size > 1 && query[:attributes].any? { |_attribute, value| value.is_a?(Array) }
21
22
  return nil
22
23
  end
23
24
 
@@ -1,5 +1,6 @@
1
+ # frozen_string_literal: true
1
2
  module Kasket
2
- VERSION = '4.4.3'
3
+ VERSION = '4.4.4'
3
4
  class Version
4
5
  MAJOR = Kasket::VERSION.split('.')[0]
5
6
  MINOR = Kasket::VERSION.split('.')[1]
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'arel'
2
3
 
3
4
  module Kasket
@@ -13,6 +14,8 @@ module Kasket
13
14
  super
14
15
  end
15
16
 
17
+ private
18
+
16
19
  def last_column=(col)
17
20
  Thread.current[:arel_visitors_to_sql_last_column] = col
18
21
  end
@@ -29,7 +32,7 @@ module Kasket
29
32
  return :unsupported if node.with
30
33
  return :unsupported if node.offset
31
34
  return :unsupported if node.lock
32
- return :unsupported if node.orders.any?
35
+ return :unsupported if ordered?(node)
33
36
  return :unsupported if node.cores.size != 1
34
37
 
35
38
  query = visit_Arel_Nodes_SelectCore(node.cores[0])
@@ -45,9 +48,9 @@ module Kasket
45
48
 
46
49
  def visit_Arel_Nodes_SelectCore(node, *_)
47
50
  return :unsupported if node.groups.any?
48
- return :unsupported if (ActiveRecord::VERSION::MAJOR < 5 ? node.having : node.havings.present?)
51
+ return :unsupported if ActiveRecord::VERSION::MAJOR < 5 ? node.having : node.havings.present?
49
52
  return :unsupported if node.set_quantifier
50
- return :unsupported if (!node.source || node.source.empty?)
53
+ return :unsupported if !node.source || node.source.empty?
51
54
  return :unsupported if node.projections.size != 1
52
55
 
53
56
  select = node.projections[0]
@@ -62,28 +65,28 @@ module Kasket
62
65
  end
63
66
 
64
67
  def visit_Arel_Nodes_Limit(node, *_)
65
- if ActiveRecord::VERSION::MAJOR < 5
66
- {:limit => node.value.to_i}
67
- else
68
- {:limit => visit(node.value).to_i}
69
- end
68
+ if ActiveRecord::VERSION::MAJOR < 5
69
+ { limit: node.value.to_i }
70
+ else
71
+ { limit: visit(node.value).to_i }
72
+ end
70
73
  end
71
74
 
72
75
  def visit_Arel_Nodes_JoinSource(node, *_)
73
76
  return :unsupported if !node.left || node.right.any?
74
- return :unsupported if !node.left.is_a?(Arel::Table)
77
+ return :unsupported unless node.left.is_a?(Arel::Table)
75
78
  visit(node.left)
76
79
  end
77
80
 
78
81
  def visit_Arel_Table(node, *_)
79
- {:from => node.name}
82
+ { from: node.name }
80
83
  end
81
84
 
82
85
  def visit_Arel_Nodes_And(node, *_)
83
86
  attributes = node.children.map { |child| visit(child) }
84
87
  return :unsupported if attributes.include?(:unsupported)
85
88
  attributes.sort! { |pair1, pair2| pair1[0].to_s <=> pair2[0].to_s }
86
- { :attributes => attributes }
89
+ { attributes: attributes }
87
90
  end
88
91
 
89
92
  def visit_Arel_Nodes_In(node, *_)
@@ -94,12 +97,12 @@ module Kasket
94
97
  end
95
98
 
96
99
  def visit_Arel_Nodes_Equality(node, *_)
97
- right = case node.right
98
- when false then 0 # This should probably be removed when Rails 3.2 is not supported anymore
99
- when nil then nil
100
- else
101
- visit(node.right)
102
- end
100
+ right =
101
+ case node.right
102
+ when false then 0 # This should probably be removed when Rails 3.2 is not supported anymore
103
+ when nil then nil
104
+ else visit(node.right)
105
+ end
103
106
  [visit(node.left), right]
104
107
  end
105
108
 
@@ -110,14 +113,13 @@ module Kasket
110
113
 
111
114
  def literal(node, *_)
112
115
  if node == '?'
113
- column, value = @binds.shift
114
- value.to_s
116
+ @binds.shift.last.to_s
115
117
  else
116
118
  node.to_s
117
119
  end
118
120
  end
119
121
 
120
- def visit_Arel_Nodes_BindParam(x, *_)
122
+ def visit_Arel_Nodes_BindParam(_x, *_)
121
123
  if ActiveRecord::VERSION::MAJOR < 5
122
124
  visit(@binds.shift[1])
123
125
  else
@@ -133,11 +135,11 @@ module Kasket
133
135
  quoted(node.val) unless node.val.nil?
134
136
  end
135
137
 
136
- def visit_TrueClass(node)
138
+ def visit_TrueClass(_node)
137
139
  1
138
140
  end
139
141
 
140
- def visit_FalseClass(node)
142
+ def visit_FalseClass(_node)
141
143
  0
142
144
  end
143
145
 
@@ -145,8 +147,13 @@ module Kasket
145
147
  @model_class.connection.quote(node)
146
148
  end
147
149
 
148
- alias :visit_String :literal
149
- alias :visit_Fixnum :literal
150
- alias :visit_Arel_Nodes_SqlLiteral :literal
150
+ # any non `id asc` ordering
151
+ def ordered?(node)
152
+ !node.orders.all? { |o| o.is_a?(Arel::Nodes::Ascending) && o.expr.name == "id" }
153
+ end
154
+
155
+ alias_method :visit_String, :literal
156
+ alias_method :visit_Fixnum, :literal
157
+ alias_method :visit_Arel_Nodes_SqlLiteral, :literal
151
158
  end
152
159
  end
@@ -1,6 +1,6 @@
1
+ # frozen_string_literal: true
1
2
  module Kasket
2
3
  module WriteMixin
3
-
4
4
  module ClassMethods
5
5
  def remove_from_kasket(ids)
6
6
  Array(ids).each do |id|
@@ -38,20 +38,25 @@ module Kasket
38
38
  end
39
39
 
40
40
  def kasket_keys(options = {})
41
- attribute_sets = [attributes.symbolize_keys]
41
+ attribute_sets = [attributes]
42
42
 
43
43
  previous_changes = options[:previous_changes] || previous_changes()
44
44
  if previous_changes.present?
45
- old_attributes = Hash[*previous_changes.map { |attribute, values| [attribute, values[0]] }.flatten].symbolize_keys
45
+ old_attributes = previous_changes.each_with_object({}) do |(attribute, (old, _)), all|
46
+ all[attribute.to_s] = old
47
+ end
46
48
  attribute_sets << old_attributes.reverse_merge(attribute_sets[0])
49
+ attribute_sets << attribute_sets[0].merge(old_attributes)
47
50
  end
48
51
 
49
52
  keys = []
50
53
  self.class.kasket_indices.each do |index|
51
- keys += attribute_sets.map do |attribute_set|
52
- key = self.class.kasket_key_for(index.map { |attribute| [attribute, attribute_set[attribute]]})
53
- index.include?(:id) ? key : [key, key + '/first']
54
- end
54
+ keys.concat(
55
+ attribute_sets.map do |attribute_set|
56
+ key = self.class.kasket_key_for(index.map { |attribute| [attribute, attribute_set[attribute.to_s]] })
57
+ index.include?(:id) ? key : [key, key + '/first']
58
+ end
59
+ )
55
60
  end
56
61
 
57
62
  keys.flatten!
@@ -85,7 +90,7 @@ module Kasket
85
90
 
86
91
  if ActiveRecord::VERSION::MAJOR == 3
87
92
  def update_column(column, value)
88
- previous_changes = {column => [attributes[column.to_s], value]}
93
+ previous_changes = { column => [attributes[column.to_s], value] }
89
94
  result = super
90
95
  clear_kasket_indices(previous_changes: previous_changes)
91
96
  result
@@ -93,7 +98,9 @@ module Kasket
93
98
  else
94
99
  def update_columns(new_attributes)
95
100
  previous_attributes = attributes
96
- previous_changes = new_attributes.each_with_object({}) { |(k, v), all| all[k] = [previous_attributes[k.to_s], v] }
101
+ previous_changes = new_attributes.each_with_object({}) do |(k, v), all|
102
+ all[k] = [previous_attributes[k.to_s], v]
103
+ end
97
104
  result = super
98
105
  clear_kasket_indices(previous_changes: previous_changes)
99
106
  result
@@ -109,7 +116,7 @@ module Kasket
109
116
  end
110
117
 
111
118
  def kasket_after_destroy
112
- Kasket.add_pending_record(self, destroyed = true)
119
+ Kasket.add_pending_record(self, _destroyed = true)
113
120
  end
114
121
 
115
122
  def committed!(*)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kasket
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.4.3
4
+ version: 4.4.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mick Staugaard
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2017-08-03 00:00:00.000000000 Z
12
+ date: 2017-08-08 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord
@@ -173,7 +173,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
173
173
  requirements:
174
174
  - - ">="
175
175
  - !ruby/object:Gem::Version
176
- version: '0'
176
+ version: 2.2.0
177
177
  required_rubygems_version: !ruby/object:Gem::Requirement
178
178
  requirements:
179
179
  - - ">="