rediska 0.0.11 → 0.1.0

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: 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