kasket 4.4.3 → 4.4.4

Sign up to get free protection for your applications and to get access to all the features.
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
  - - ">="