rediska 0.0.11 → 0.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8f8e37b9ab4297926f687d271ce9155ea1680a31
4
- data.tar.gz: 36bf08ab258f141e165c23ee13b7b3a326b90b06
3
+ metadata.gz: 2e4ac28eb56bf3a2cab47dc9b091b7182acc88b8
4
+ data.tar.gz: 5d6d48bc843232644fbfbf92063a7785a6db947e
5
5
  SHA512:
6
- metadata.gz: effd83624ed9df79f4d456b9942abfe4a11bae414472dd251db10661fb4e1118368ecfcae6d6f5f3a323df2c10f2b48a950151cd0fec2b9253e0031877a14476
7
- data.tar.gz: 25f44ac315938e63414e5cf70b814eabd2ff82bc74a7f5701b68a9f312bc12037bec43a3dc3a7c871c64edb8abf93d86d155a1074507f186cea347e916e3b3ee
6
+ metadata.gz: 7b149f856ed62eb989464acdf464424d2c870a752b5e00014e0e83b7c3cb48c66a375d819a792641b7989f19868dc60d5f6b86d7f89a4f5ce335d5e0d8255b99
7
+ data.tar.gz: 5c5cfffc7e706fe27dee1ea447d36a9c58cff8f63e906060c516f1c9e4b4d190f618cc79a2b4746ae464091231e2a3aa521ae4192ab7eddd3307a8febb994971
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rediska (0.0.11)
4
+ rediska (0.1.0)
5
5
  redis (~> 3.0.0)
6
6
 
7
7
  GEM
@@ -16,22 +16,26 @@ GEM
16
16
  diff-lcs (1.2.5)
17
17
  docile (1.1.3)
18
18
  json (1.8.1)
19
- mime-types (2.2)
20
- multi_json (1.9.2)
21
- rake (10.2.2)
19
+ mime-types (2.3)
20
+ multi_json (1.10.1)
21
+ rake (10.3.2)
22
22
  rdoc (4.1.1)
23
23
  json (~> 1.4)
24
24
  redis (3.0.7)
25
25
  rest-client (1.6.7)
26
26
  mime-types (>= 1.16)
27
- rspec (2.14.1)
28
- rspec-core (~> 2.14.0)
29
- rspec-expectations (~> 2.14.0)
30
- rspec-mocks (~> 2.14.0)
31
- rspec-core (2.14.8)
32
- rspec-expectations (2.14.5)
33
- diff-lcs (>= 1.1.3, < 2.0)
34
- rspec-mocks (2.14.6)
27
+ rspec (3.0.0)
28
+ rspec-core (~> 3.0.0)
29
+ rspec-expectations (~> 3.0.0)
30
+ rspec-mocks (~> 3.0.0)
31
+ rspec-core (3.0.0)
32
+ rspec-support (~> 3.0.0)
33
+ rspec-expectations (3.0.0)
34
+ diff-lcs (>= 1.2.0, < 2.0)
35
+ rspec-support (~> 3.0.0)
36
+ rspec-mocks (3.0.0)
37
+ rspec-support (~> 3.0.0)
38
+ rspec-support (3.0.0)
35
39
  simplecov (0.8.2)
36
40
  docile (~> 1.1.0)
37
41
  multi_json
@@ -40,7 +44,7 @@ GEM
40
44
  term-ansicolor (1.3.0)
41
45
  tins (~> 1.0)
42
46
  thor (0.19.1)
43
- tins (1.0.1)
47
+ tins (1.3.0)
44
48
 
45
49
  PLATFORMS
46
50
  ruby
@@ -50,4 +54,4 @@ DEPENDENCIES
50
54
  rake
51
55
  rdoc
52
56
  rediska!
53
- rspec (~> 2.14)
57
+ rspec
@@ -0,0 +1,25 @@
1
+ module Rediska
2
+ module CommandExecutor
3
+ def write(command)
4
+ meffod = command.shift.to_s.downcase.to_sym
5
+
6
+ if in_multi && !(TRANSACTION_COMMANDS.include? meffod) # queue commands
7
+ queued_commands << [meffod, *command]
8
+ reply = 'QUEUED'
9
+ elsif respond_to?(meffod)
10
+ reply = send(meffod, *command)
11
+ else
12
+ raise Redis::CommandError, "ERR unknown command '#{meffod}'"
13
+ end
14
+
15
+ if reply == true
16
+ reply = 1
17
+ elsif reply == false
18
+ reply = 0
19
+ end
20
+
21
+ replies << reply
22
+ nil
23
+ end
24
+ end
25
+ end
@@ -1,13 +1,20 @@
1
1
  require 'rediska/databases/memory'
2
2
  require 'rediska/databases/pstore'
3
+ require 'rediska/sort_method'
3
4
  require 'rediska/sorted_set_argument_handler'
4
5
  require 'rediska/sorted_set_store'
5
6
  require 'rediska/zset'
6
7
  require 'rediska/driver'
8
+ require 'rediska/command_executor'
9
+ require 'rediska/transaction_commands'
7
10
 
8
11
  module Rediska
9
12
  class Connection
10
13
  include Driver
14
+ include SortMethod
15
+ include TransactionCommands
16
+ include CommandExecutor
17
+
11
18
 
12
19
  class << self
13
20
  def databases
@@ -63,5 +70,5 @@ module Rediska
63
70
  raise ArgumentError, "invalid database type: #{Rediska.configuration.database}"
64
71
  end
65
72
  end
66
- end
73
+ end
67
74
  end
@@ -1,6 +1,6 @@
1
1
  module Rediska
2
2
  module Driver
3
- attr_accessor :buffer, :database_id
3
+ attr_accessor :database_id
4
4
  attr_writer :replies
5
5
 
6
6
  def replies
@@ -20,25 +20,6 @@ module Rediska
20
20
  def timeout=(usecs)
21
21
  end
22
22
 
23
- def write(command)
24
- meffod = command.shift.to_s.downcase.to_sym
25
- if respond_to?(meffod)
26
- reply = send(meffod, *command)
27
- else
28
- raise Redis::CommandError, "ERR unknown command #{meffod.upcase}"
29
- end
30
-
31
- if reply == true
32
- reply = 1
33
- elsif reply == false
34
- reply = 0
35
- end
36
-
37
- replies << reply
38
- buffer << reply if buffer && meffod != :multi
39
- nil
40
- end
41
-
42
23
  def read
43
24
  replies.shift
44
25
  end
@@ -47,8 +28,6 @@ module Rediska
47
28
  # * blpop
48
29
  # * brpop
49
30
  # * brpoplpush
50
- # * discard
51
- # * sort
52
31
  # * subscribe
53
32
  # * psubscribe
54
33
  # * publish
@@ -159,8 +138,9 @@ module Rediska
159
138
  def hdel(key, field)
160
139
  field = field.to_s
161
140
  data_type_check(key, Hash)
162
- data[key] && data[key].delete(field)
141
+ deleted = data[key] && data[key].delete(field)
163
142
  remove_key_for_empty_collection(key)
143
+ deleted ? 1 : 0
164
144
  end
165
145
 
166
146
  def hkeys(key)
@@ -307,7 +287,7 @@ module Rediska
307
287
  def rpoplpush(key1, key2)
308
288
  data_type_check(key1, Array)
309
289
  rpop(key1).tap do |elem|
310
- lpush(key2, elem)
290
+ lpush(key2, elem) unless elem.nil?
311
291
  end
312
292
  end
313
293
 
@@ -590,9 +570,23 @@ module Rediska
590
570
  set(key, value)
591
571
  end
592
572
 
593
- def set(key, value)
573
+ def set(key, value, *array_options)
574
+ option_nx = array_options.delete('NX')
575
+ option_xx = array_options.delete('XX')
576
+
577
+ return false if option_nx && option_xx
578
+
579
+ return false if option_nx && exists(key)
580
+ return false if option_xx && !exists(key)
581
+
594
582
  data[key] = value.to_s
595
583
 
584
+ options = Hash[array_options.each_slice(2).to_a]
585
+ ttl_in_seconds = options['EX'] if options['EX']
586
+ ttl_in_seconds = options['PX'] / 1000.0 if options['PX']
587
+
588
+ expire(key, ttl_in_seconds) if ttl_in_seconds
589
+
596
590
  'OK'
597
591
  end
598
592
 
@@ -646,10 +640,6 @@ module Rediska
646
640
  true
647
641
  end
648
642
 
649
- def sort(key)
650
- # TODO: Implement
651
- end
652
-
653
643
  def incr(key)
654
644
  data.merge!({ key => (data[key].to_i + 1).to_s || '1'})
655
645
  data[key].to_i
@@ -690,25 +680,6 @@ module Rediska
690
680
  def slaveof(host, port)
691
681
  end
692
682
 
693
- def exec
694
- buffer.tap {|x| self.buffer = nil }
695
- end
696
-
697
- def multi
698
- self.buffer = []
699
- yield if block_given?
700
-
701
- 'OK'
702
- end
703
-
704
- def watch(_)
705
- 'OK'
706
- end
707
-
708
- def unwatch
709
- 'OK'
710
- end
711
-
712
683
  def scan(start_cursor, *args)
713
684
  match = '*'
714
685
  count = 10
@@ -896,6 +867,17 @@ module Rediska
896
867
  range.size
897
868
  end
898
869
 
870
+ def zremrangebyrank(key, start, stop)
871
+ data_type_check(key, ZSet)
872
+ return 0 unless data[key]
873
+
874
+ sorted_elements = data[key].sort_by { |k, v| v }
875
+ start = sorted_elements.length if start > sorted_elements.length
876
+ elements_to_delete = sorted_elements[start..stop]
877
+ elements_to_delete.each { |elem, rank| data[key].delete(elem) }
878
+ elements_to_delete.size
879
+ end
880
+
899
881
  def zinterstore(out, *args)
900
882
  data_type_check(out, ZSet)
901
883
  args_handler = SortedSetArgumentHandler.new(args)
@@ -910,14 +892,6 @@ module Rediska
910
892
  data[out].size
911
893
  end
912
894
 
913
- def zremrangebyrank(key, start, stop)
914
- sorted_elements = data[key].sort_by { |k, v| v }
915
- start = sorted_elements.length if start > sorted_elements.length
916
- elements_to_delete = sorted_elements[start..stop]
917
- elements_to_delete.each { |elem, rank| data[key].delete(elem) }
918
- elements_to_delete.size
919
- end
920
-
921
895
  private
922
896
  def raise_argument_error(command, match_string = command)
923
897
  error_message = if %w(hmset mset_odd).include?(match_string.downcase)
@@ -0,0 +1,108 @@
1
+ # Codes are mostly referenced from MockRedis' implementation.
2
+ module Rediska
3
+ module SortMethod
4
+ def sort(key, *redis_options_array)
5
+ return [] unless key
6
+
7
+ unless %w(list set zset).include? type(key)
8
+ warn "Operation against a key holding the wrong kind of value: Expected list, set or zset at #{key}."
9
+ raise Redis::CommandError.new('WRONGTYPE Operation against a key holding the wrong kind of value')
10
+ end
11
+
12
+ options = extract_options_from(redis_options_array)
13
+
14
+ projected = project(data[key], options[:by], options[:get])
15
+ sorted = sort_by(projected, options[:order])
16
+ sliced = slice(sorted, options[:limit])
17
+
18
+ options[:store] ? rpush(options[:store], sliced) : sliced.flatten(1)
19
+ end
20
+
21
+ private
22
+ ASCENDING_SORT = Proc.new { |a, b| a.first <=> b.first }
23
+ DESCENDING_SORT = Proc.new { |a, b| b.first <=> a.first }
24
+
25
+ def extract_options_from(options_array)
26
+ options = {
27
+ limit: [],
28
+ order: 'ASC',
29
+ get: []
30
+ }
31
+
32
+ if options_array.first == 'BY'
33
+ options_array.shift
34
+ options[:by] = options_array.shift
35
+ end
36
+
37
+ if options_array.first == 'LIMIT'
38
+ options_array.shift
39
+ options[:limit] = [options_array.shift, options_array.shift]
40
+ end
41
+
42
+ while options_array.first == 'GET'
43
+ options_array.shift
44
+ options[:get] << options_array.shift
45
+ end
46
+
47
+ if %w(ASC DESC ALPHA).include?(options_array.first)
48
+ options[:order] = options_array.shift
49
+ options[:order] = 'ASC' if options[:order] == 'ALPHA'
50
+ end
51
+
52
+ if options_array.first == 'STORE'
53
+ options_array.shift
54
+ options[:store] = options_array.shift
55
+ end
56
+
57
+ options
58
+ end
59
+
60
+ def project(enumerable, by, get_patterns)
61
+ enumerable.map do |*elements|
62
+ element = elements.flatten.first
63
+ weight = by ? lookup_from_pattern(by, element) : element
64
+ value = element
65
+
66
+ if get_patterns.length > 0
67
+ value = get_patterns.map do |pattern|
68
+ pattern == '#' ? element : lookup_from_pattern(pattern, element)
69
+ end
70
+ value = value.first if value.length == 1
71
+ end
72
+
73
+ [weight, value]
74
+ end
75
+ end
76
+
77
+ def sort_by(projected, direction)
78
+ sorter =
79
+ case direction.upcase
80
+ when 'DESC'
81
+ DESCENDING_SORT
82
+ when 'ASC', 'ALPHA'
83
+ ASCENDING_SORT
84
+ else
85
+ raise "Invalid direction '#{direction}'"
86
+ end
87
+
88
+ projected.sort(&sorter).map(&:last)
89
+ end
90
+
91
+ def slice(sorted, limit)
92
+ skip = limit.first || 0
93
+ take = limit.last || sorted.length
94
+
95
+ sorted[skip...(skip + take)] || sorted
96
+ end
97
+
98
+ def lookup_from_pattern(pattern, element)
99
+ key = pattern.sub('*', element)
100
+
101
+ if (hash_parts = key.split('->')).length > 1
102
+ hget hash_parts.first, hash_parts.last
103
+ else
104
+ get key
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,83 @@
1
+ module Rediska
2
+ TRANSACTION_COMMANDS = [:discard, :exec, :multi, :watch, :unwatch]
3
+
4
+ module TransactionCommands
5
+ def self.included(klass)
6
+ klass.class_eval do
7
+ def self.queued_commands
8
+ @queued_commands ||= Hash.new {|h,k| h[k] = [] }
9
+ end
10
+
11
+ def self.in_multi
12
+ @in_multi ||= Hash.new{|h,k| h[k] = false}
13
+ end
14
+
15
+ def queued_commands
16
+ self.class.queued_commands[database_instance_key]
17
+ end
18
+
19
+ def queued_commands=(cmds)
20
+ self.class.queued_commands[database_instance_key] = cmds
21
+ end
22
+
23
+ def in_multi
24
+ self.class.in_multi[database_instance_key]
25
+ end
26
+
27
+ def in_multi=(multi_state)
28
+ self.class.in_multi[database_instance_key] = multi_state
29
+ end
30
+ end
31
+ end
32
+
33
+ def discard
34
+ unless in_multi
35
+ raise Redis::CommandError, 'ERR DISCARD without MULTI'
36
+ end
37
+
38
+ self.in_multi = false
39
+ self.queued_commands = []
40
+
41
+ 'OK'
42
+ end
43
+
44
+ def exec
45
+ unless in_multi
46
+ raise Redis::CommandError, 'ERR EXEC without MULTI'
47
+ end
48
+
49
+ responses = queued_commands.map do |cmd|
50
+ begin
51
+ send(*cmd)
52
+ rescue => e
53
+ e
54
+ end
55
+ end
56
+
57
+ self.queued_commands = [] # reset queued_commands
58
+ self.in_multi = false # reset in_multi state
59
+
60
+ responses
61
+ end
62
+
63
+ def multi
64
+ if in_multi
65
+ raise Redis::CommandError, 'ERR MULTI calls can not be nested'
66
+ end
67
+
68
+ self.in_multi = true
69
+
70
+ yield(self) if block_given?
71
+
72
+ 'OK'
73
+ end
74
+
75
+ def watch(_)
76
+ 'OK'
77
+ end
78
+
79
+ def unwatch
80
+ 'OK'
81
+ end
82
+ end
83
+ end