redis_rds 0.1.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: fe5fdbd19701e3212b3dd6f36b9ccb7717ab60d9
4
+ data.tar.gz: fbe1a8aa69abecd838a8659a3175114135c67afb
5
+ SHA512:
6
+ metadata.gz: 6fab513e4524e3ef4375540a52fac2f7efac0a77b2bd7fc7c9a0a12e0cfacc0a8fcb2adbd0921ccb27a67d07a69de9d07261ec566551bc29218597544be3a8f1
7
+ data.tar.gz: 72cae17306552b4f7807f4163caee1950d73eac99d99be99ff61abbf096d3178eed31ac0479120ab3ca69b5802ef1435b952119749e2c9f93a3088709db483a2
data/.coveralls.yml ADDED
@@ -0,0 +1,2 @@
1
+ service_name: travis-ci
2
+
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # ignore vim tmp and swap files
11
+ *~
12
+ *.swp
13
+ *.swo
14
+
15
+ .DS_Store
16
+ .rubocop-*
17
+ redis_rds.sublime-workspace
18
+ README.md-u
data/.rubocop.yml ADDED
@@ -0,0 +1,2 @@
1
+ inherit_from:
2
+ - https://raw.githubusercontent.com/barcoo/barcoop/master/rubocop.yml
data/.travis.yml ADDED
@@ -0,0 +1,8 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.2
5
+ - 2.1
6
+ services:
7
+ - redis-server
8
+ before_install: gem install bundler -v 1.12.5
data/CHANGELOG.md ADDED
@@ -0,0 +1,29 @@
1
+ # Changelog
2
+
3
+ ##0.1.1
4
+
5
+ - [6617250](https://github.com/barcoo/redis_rds/commit/6617250) *2016-07-24* __move tests to test/redis_rds__ (Sergio Medina)
6
+ - [48d8c0b](https://github.com/barcoo/redis_rds/commit/48d8c0b) *2016-07-24* __run tests in random order__ (Sergio Medina)
7
+ - [2fe8944](https://github.com/barcoo/redis_rds/commit/2fe8944) *2016-07-24* __require only active record core extensions__ (Sergio Medina)
8
+ - [9cf87a8](https://github.com/barcoo/redis_rds/commit/9cf87a8) *2016-07-24* __ignore vim temp and swap files__ (Sergio Medina)
9
+ - [96b541b](https://github.com/barcoo/redis_rds/commit/96b541b) *2016-07-23* __drop rails dependency and use default bundler gem scaffolding__ (Sergio Medina)
10
+ - [2cce40e](https://github.com/barcoo/redis_rds/commit/2cce40e) *2016-07-19* __fix broken link__ (Sergio Medina)
11
+ - [9afe525](https://github.com/barcoo/redis_rds/commit/9afe525) *2016-07-19* __build status added, removing it from the roadmap__ (Sergio Medina)
12
+ - [aecb26b](https://github.com/barcoo/redis_rds/commit/aecb26b) *2016-07-19* __use travis badge for master branch__ (Sergio Medina)
13
+ - [db3ffb2](https://github.com/barcoo/redis_rds/commit/db3ffb2) *2016-07-19* __add travis CI config__ (Sergio Medina)
14
+ - [0dc4578](https://github.com/barcoo/redis_rds/commit/0dc4578) *2016-07-19* __move RedisSingleton.clear_test_db to setup in test_helper__ (Sergio Medina)
15
+
16
+ ##0.1.0
17
+
18
+ - [b97c0c4](https://github.com/barcoo/redis_rds/commit/b97c0c4) *2016-07-18* __bump version to 0.1.0__ (Sergio Medina)
19
+ - [4a29c74](https://github.com/barcoo/redis_rds/commit/4a29c74) *2016-07-18* __remove configuration through YAML files__ (Sergio Medina)
20
+ - [c8534b3](https://github.com/barcoo/redis_rds/commit/c8534b3) *2016-07-18* __small changes to Roadmap__ (Sergio Medina)
21
+
22
+ ##0.0.4
23
+
24
+
25
+
26
+ ##0.0.3
27
+
28
+ - [6dd41ea](https://github.com/barcoo/redis_rds/commit/6dd41ea) *2016-07-17* __version bump__ (Sergio Medina)
29
+ - [0431cd1](https://github.com/barcoo/redis_rds/commit/0431cd1) *2016-07-17* __ignore README.md-u__ (Sergio Medina)
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in redis_rds.gemspec
4
+ gemspec
5
+
6
+ gem 'simplecov', require: false, group: :test
7
+ gem 'coveralls', require: false
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Checkitmobile GmbH
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,91 @@
1
+ # RedisRds
2
+
3
+ [![GitHub release](https://img.shields.io/badge/release-0.1.1-blue.png)](https://github.com/barcoo/redis_rds/releases/tag/0.1.1)
4
+ [![Build Status](https://travis-ci.org/barcoo/redis_rds.svg?branch=master)](https://travis-ci.org/barcoo/redis_rds)
5
+ [![Coverage Status](https://coveralls.io/repos/github/barcoo/redis_rds/badge.svg?branch=master&update=now)](https://coveralls.io/github/barcoo/redis_rds?branch=master)
6
+
7
+ Ruby data structures stored in Redis.
8
+
9
+ RedisRds provides Redis-backed Ruby interfaces for the following data structures:
10
+
11
+ * String
12
+ * Hash
13
+ * ExpirableHash
14
+ * NestedHash
15
+ * List
16
+ * Mutex
17
+ * Set
18
+ * SortedSet
19
+ * SortedStringSet
20
+
21
+ ## Installation
22
+
23
+ Add to your gemfile:
24
+
25
+ ```ruby
26
+ gem 'redis_rds', git: 'git@github.com:barcoo/redis_rds.git'
27
+ ```
28
+
29
+ ## Usage
30
+
31
+ Best is to check [the tests](test/) to see how to use the data structures.
32
+ Here are two examples, for Mutex and String:
33
+
34
+ ```ruby
35
+ # configure RedisRds by passing either a connection
36
+ RedisRds.configure(connection: my_redis_connection, namespace: 'my_app')
37
+ # or connection parameters
38
+ RedisRds.configure(
39
+ host: 'localhost',
40
+ db: 1,
41
+ port: 6379,
42
+ namespace: 'my_app',
43
+ )
44
+ ```
45
+
46
+ ```ruby
47
+ my_semaphore = RedisRds::Mutex.new('a_key')
48
+ my_semaphore.synchronize do
49
+ # do something with a shared resource
50
+ write_file()
51
+ end # mutex released, another thread can use the resource
52
+ ```
53
+
54
+ ```ruby
55
+ string_a = RedisRds::String.new('some_key')
56
+ string_a = 'hello'
57
+
58
+ string_b = RedisRds::String.new('some_key')
59
+ puts string_b
60
+ # 'hello'
61
+ ```
62
+
63
+ ## Contributing
64
+
65
+ Take a look at the [Roadmap](doc/ROADMAP.md) and lint your code using [rubocop](https://github.com/bbatsov/rubocop) and our [rubocop.yml](.rubocop.yml) file.
66
+
67
+ To run the tests, start a Redis server on localhost and run `bundle exec rake test`.
68
+
69
+ To contribute:
70
+
71
+ 1. Fork it
72
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
73
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
74
+ 4. Push to the branch (`git push origin my-new-feature`)
75
+ 5. Create new Pull Request
76
+
77
+ This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
78
+
79
+ ## Release
80
+
81
+ ___Note___: eventually use one of the popular git release scripts to tag, create tag notes, etc., based on git changelog.
82
+
83
+ When you want to create a new release, use the rake task ```cim:release``` (in the main Rakefile)
84
+
85
+ ```shell
86
+ bundle exec rake cim:release
87
+ ```
88
+
89
+ ## License
90
+
91
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,99 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << 'test'
6
+ t.libs << 'lib'
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ end
9
+
10
+ task default: :test
11
+
12
+ namespace :cim do
13
+ desc 'Tags, updates README, and CHANGELOG and pushes to Github. Requires ruby-git'
14
+ task :release do
15
+ tasks = ['cim:assert_clean_repo', 'cim:git_fetch', 'cim:assert_version', 'cim:update_readme', 'cim:update_changelog', 'cim:commit_changes', 'cim:tag']
16
+ begin
17
+ tasks.each { |task| Rake::Task[task].invoke }
18
+ `git push && git push origin '#{RedisRds::VERSION}'`
19
+ rescue => error
20
+ puts ">>> ERROR: #{error}; might want to reset your repository"
21
+ end
22
+ end
23
+
24
+ desc 'Fails if the current repository is not clean'
25
+ task :assert_clean_repo do
26
+ status = `git status -s`.chomp.strip
27
+ if status.empty?
28
+ status = `git log origin/master..HEAD`.chomp.strip # check if we have unpushed commits
29
+ if status.empty?
30
+ puts '>>> Repository is clean!'
31
+ else
32
+ puts '>>> Please push your committed changes before releasing!'
33
+ exit -1
34
+ end
35
+ else
36
+ puts '>>> Please stash or commit your changes before releasing!'
37
+ exit -1
38
+ end
39
+ end
40
+
41
+ desc 'Fetches latest tags/commits'
42
+ task :git_fetch do
43
+ puts '>>> Fetching latest git refs'
44
+ `git fetch --tags`
45
+ end
46
+
47
+ desc 'Fails if the current gem version is not greater than the last tag'
48
+ task :assert_version do
49
+ latest = `git describe --abbrev=0`.chomp.strip
50
+ current = RedisRds::VERSION
51
+
52
+ unless Gem::Version.new(current) > Gem::Version.new(latest)
53
+ puts ">>> Latest tagged version is #{latest}; make sure gem version (#{current}) is greater!"
54
+ exit -1
55
+ end
56
+ end
57
+
58
+ desc 'Updates README with latest version'
59
+ task :update_readme do
60
+ puts '>>> Updating README.md'
61
+ replace = %([![GitHub release](https://img.shields.io/badge/release-#{RedisRds::VERSION}-blue.png)](https://github.com/barcoo/redis_rds/releases/tag/#{RedisRds::VERSION}))
62
+
63
+ `sed -i -u 's@^\\[\\!\\[GitHub release\\].*$@#{replace}@' README.md`
64
+ end
65
+
66
+ desc 'Updates CHANGELOG with commit log from last tag to this one'
67
+ task :update_changelog do
68
+ puts '>>> Updating CHANGELOG.md'
69
+ latest = `git describe --abbrev=0`.chomp.strip
70
+ log = `git log --pretty=format:'- [%h](https://github.com/barcoo/redis_rds/commit/%h) *%ad* __%s__ (%an)' --date=short '#{latest}'..HEAD`.chomp
71
+
72
+ changelog = File.open('.CHANGELOG.md', 'w')
73
+ changelog.write("# Changelog\n\n###{RedisRds::VERSION}\n\n#{log}\n\n")
74
+ File.open('CHANGELOG.md', 'r') do |file|
75
+ file.readline # skip first two lines
76
+ file.readline
77
+ while buffer = file.read(2048)
78
+ changelog.write(buffer)
79
+ end
80
+ end
81
+
82
+ changelog.close
83
+ `mv '.CHANGELOG.md' 'CHANGELOG.md'`
84
+ end
85
+
86
+ desc 'Commits the README/CHANGELOG changes'
87
+ task :commit_changes do
88
+ puts '>>> Committing updates to README/CHANGELOG'
89
+ `git commit -am'Updated README.md and CHANGELOG.md on new release'`
90
+ end
91
+
92
+ desc 'Creates and pushes the tag to git'
93
+ task :tag do
94
+ puts '>>> Tagging'
95
+ message = STDOUT.print '>>> Please enter a tag message: '
96
+ input = STDIN.gets.strip.tr("'", "\'")
97
+ `git tag -a '#{RedisRds::VERSION}' -m '#{input}'`
98
+ end
99
+ end
data/bin/console ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'redis_rds'
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ require 'pry'
10
+ Pry.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/doc/ROADMAP.md ADDED
@@ -0,0 +1,10 @@
1
+ # Roadmap
2
+
3
+ - Add to RubyGems
4
+ - Remove ActiveSupport dependency
5
+ - Add documentation (use yard)
6
+ - Add support for Redis authentication (or simply remove config by params and only accept a connection?)
7
+ - Implement lazy enumerators (see Hash for instance)
8
+ - Improve lib configuration (use Configuration class, allow multiple instances)
9
+ - Use hashes for optional parameters (f.i. see Mutex::initialize)
10
+ - Add support for latest Redis commands (f.i. Geospatial commands)
@@ -0,0 +1,46 @@
1
+ module RedisRds
2
+ class Composite
3
+ extend Forwardable
4
+
5
+ attr_reader :members
6
+ def_delegators :@members, :[], :[]=, :assoc, :size, :empty?, :each, :include?, :length, :merge
7
+
8
+ def initialize(key)
9
+ @key = key
10
+ @members = {}.with_indifferent_access
11
+ end
12
+
13
+ def add(key, klass)
14
+ object = klass.new(format_member_key(key))
15
+ @members[key] = object
16
+ end
17
+
18
+ def format_member_key(key)
19
+ return "#{@key}:#{key}"
20
+ end
21
+ private :format_member_key
22
+
23
+ def delete(key)
24
+ if @members.key?(key)
25
+ @members[key].delete
26
+ @members.delete(key)
27
+ end
28
+ end
29
+
30
+ def clear
31
+ @members.each do |k, _|
32
+ self.delete(k)
33
+ end
34
+ @members.clear
35
+ end
36
+
37
+ def method_missing(*args)
38
+ result = nil
39
+
40
+ key = args.first
41
+ result = @members[key] if key.present?
42
+
43
+ return result
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,36 @@
1
+ require 'yaml'
2
+ require 'redis'
3
+
4
+ module RedisRds
5
+ # Configuration defaults
6
+ @config = {
7
+ host: 'localhost',
8
+ db: 1,
9
+ port: 6379,
10
+ timeout: 30,
11
+ thread_safe: true,
12
+ # FIXME: add username and password
13
+ namespace: 'testns',
14
+ connection: nil
15
+ }
16
+
17
+ @valid_config_keys = @config.keys
18
+
19
+ # Configure through hash
20
+ def self.configure(opts = {})
21
+ opts.each { |k, v| @config[k.to_sym] = v if @valid_config_keys.include? k.to_sym }
22
+
23
+ if opts[:connection].present?
24
+ connection = opts[:connection]
25
+ else
26
+ config[:db] = config[:db].to_i
27
+ connection = Redis.new(config)
28
+ end
29
+
30
+ RedisRds::Object.configure(connection: connection, namespace: opts[:namespace])
31
+ end
32
+
33
+ def self.config
34
+ @config
35
+ end
36
+ end
@@ -0,0 +1,61 @@
1
+ module RedisRds
2
+ class ExpirableHash < RedisRds::Hash
3
+ EXPIRY_KEY_SUFFIX = '__expiry__'.freeze
4
+
5
+ def get(key)
6
+ result = nil
7
+ value, expires_at = connection.hmget(@redis_key, key, expiry_key(key))
8
+ if expired?(expires_at)
9
+ remove(key)
10
+ else
11
+ result = value
12
+ end
13
+
14
+ return result
15
+ end
16
+
17
+ def set(key, value)
18
+ connection.multi do |redis|
19
+ redis.hset(@redis_key, key, value)
20
+ redis.hdel(@redis_key, expiry_key(key))
21
+ end
22
+ end
23
+
24
+ def setex(key, value, expiry)
25
+ expires_at = Time.now.to_i + expiry
26
+ connection.hmset(@redis_key, key, value, expiry_key(key), expires_at)
27
+ end
28
+
29
+ def expiry_key(key)
30
+ return "#{key}#{EXPIRY_KEY_SUFFIX}"
31
+ end
32
+
33
+ def remove(key)
34
+ connection.multi do |redis|
35
+ redis.hdel(@redis_key, key)
36
+ redis.hdel(@redis_key, expiry_key(key))
37
+ end
38
+ end
39
+
40
+ def all
41
+ hash = ::Hash[super().group_by do |key, _value|
42
+ if key.ends_with?(EXPIRY_KEY_SUFFIX)
43
+ key[0...(key.length - EXPIRY_KEY_SUFFIX.length)]
44
+ else
45
+ key
46
+ end
47
+ end.map { |key, values| [key, values.map(&:second)] }]
48
+
49
+ expired, valid = hash.partition { |_key, values| expired?(values.second) }
50
+ expired.each { |key, _| remove(key) }
51
+
52
+ return ::Hash[valid.map { |key, values| [key, values.first] }]
53
+ end
54
+
55
+ def expired?(expires_at)
56
+ return false if expires_at.nil?
57
+ return Time.now.to_i >= expires_at.to_i
58
+ end
59
+ private :expired?
60
+ end
61
+ end
@@ -0,0 +1,76 @@
1
+ module RedisRds
2
+ class Hash < RedisRds::Object
3
+ include Enumerable
4
+
5
+ def get(key)
6
+ return connection.hget(@redis_key, key)
7
+ end
8
+ alias [] get
9
+
10
+ def set(key, value)
11
+ return connection.hset(@redis_key, key, value)
12
+ end
13
+ alias []= set
14
+
15
+ def mset(*args)
16
+ return connection.hmset(@redis_key, *args)
17
+ end
18
+
19
+ def mget(*_args)
20
+ return connection.hmget(@redis_key, *arg)
21
+ end
22
+
23
+ def setnx(key, value)
24
+ return connection.hsetnx(@redis_key, key, value)
25
+ end
26
+
27
+ def remove(key)
28
+ return connection.hdel(@redis_key, key)
29
+ end
30
+
31
+ def incrby(key, increment)
32
+ return connection.hincrby(@redis_key, key, increment)
33
+ end
34
+
35
+ def key?(key)
36
+ return connection.hexists(@redis_key, key)
37
+ end
38
+
39
+ def incr(key)
40
+ return incrby(key, 1)
41
+ end
42
+
43
+ def decr(key)
44
+ return incrby(key, -1)
45
+ end
46
+
47
+ def decrby(key, decrement)
48
+ return incrby(key, -decrement)
49
+ end
50
+
51
+ def all
52
+ return (getall || {}).with_indifferent_access
53
+ end
54
+
55
+ def getall
56
+ return connection.hgetall(@redis_key)
57
+ end
58
+
59
+ def values
60
+ return all.values
61
+ end
62
+
63
+ def keys
64
+ return all.keys
65
+ end
66
+
67
+ def to_json
68
+ return all.to_json
69
+ end
70
+
71
+ # TODO: Implement lazy enumerator
72
+ def each(&block)
73
+ return all.each(&block)
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,50 @@
1
+ module RedisRds
2
+ class List < RedisRds::Object
3
+ include Enumerable
4
+
5
+ def size
6
+ return connection.llen(@redis_key)
7
+ end
8
+
9
+ def empty?
10
+ return self.size == 0 # rubocop:disable Style/ZeroLengthPredicate
11
+ end
12
+
13
+ def get(start, stop = -1)
14
+ return connection.lrange(@redis_key, start, stop)
15
+ end
16
+
17
+ def rpush(elems)
18
+ return connection.rpush(@redis_key, Array.wrap(elems))
19
+ end
20
+
21
+ def lpush(elems)
22
+ return connection.lpush(@redis_key, Array.wrap(elems))
23
+ end
24
+
25
+ def lpop(length = 1, force = false)
26
+ lua_script = "
27
+ local length = tonumber(ARGV[1]);
28
+ if (ARGV[2] ~= 'true' or redis.call('llen', KEYS[1])>=length) then
29
+ local result = redis.call('lrange', KEYS[1], 0, length - 1);
30
+ redis.call('ltrim', KEYS[1], length, - 1);
31
+ return result
32
+ else return ''
33
+ end"
34
+ result = connection.eval(lua_script, [@redis_key], [length, force])
35
+ result = [] if result.blank?
36
+
37
+ return result
38
+ end
39
+
40
+ def each(&block)
41
+ return get(0).each(&block)
42
+ end
43
+
44
+ def clear
45
+ # if start > end redis clears the list
46
+ # http://redis.io/commands/ltrim
47
+ return connection.ltrim(@redis_key, 1, 0)
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,56 @@
1
+ module RedisRds
2
+ class Mutex < RedisRds::String
3
+ DEFAULT_EXPIRY = 60.seconds
4
+
5
+ attr_reader :id, :expiry
6
+
7
+ def initialize(id, expiry = DEFAULT_EXPIRY, owner = '')
8
+ super("#{namespace}:mutex:#{id}")
9
+
10
+ @id = id
11
+ @expiry = expiry
12
+ @owner = owner.blank? ? generate_owner : owner
13
+ end
14
+
15
+ def lock
16
+ connection.set(@redis_key, @owner, ex: @expiry, nx: true)
17
+ return locked?
18
+ end
19
+
20
+ def locked?
21
+ return connection.get(@redis_key) == @owner
22
+ end
23
+ alias owned? locked?
24
+
25
+ def release
26
+ self.delete if owned?
27
+ end
28
+
29
+ def serialize
30
+ [@id, @expiry, @owner]
31
+ end
32
+
33
+ def synchronize
34
+ if self.lock
35
+ begin
36
+ yield if block_given?
37
+ ensure
38
+ self.release
39
+ end
40
+ end
41
+ end
42
+
43
+ def generate_owner
44
+ now = Time.now.to_f
45
+ random = Random.new(now)
46
+ return Digest::MD5.hexdigest(random.rand(now).to_s)
47
+ end
48
+ private :generate_owner
49
+
50
+ class << self
51
+ def unserialize(serialized)
52
+ return self.new(*serialized)
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,38 @@
1
+ module RedisRds
2
+ class NestedHash < RedisRds::Hash
3
+ SEPARATOR = ':'.freeze
4
+
5
+ def setnx(*keys, value)
6
+ return super(format_key(keys), value)
7
+ end
8
+
9
+ def incrby(*keys, increment)
10
+ return super(format_key(keys), increment)
11
+ end
12
+
13
+ def key?(_key)
14
+ return super(format_key(keys))
15
+ end
16
+
17
+ def get(*keys)
18
+ super(format_key(keys))
19
+ end
20
+
21
+ def set(*keys, value)
22
+ super(format_key(keys), value)
23
+ end
24
+
25
+ def setex(*keys, value, expiry)
26
+ super(format_key(keys), value, expiry)
27
+ end
28
+
29
+ def remove(*keys)
30
+ super(format_key(keys))
31
+ end
32
+
33
+ def format_key(keys)
34
+ return Array.wrap(keys).join(SEPARATOR)
35
+ end
36
+ private :format_key
37
+ end
38
+ end
@@ -0,0 +1,74 @@
1
+ module RedisRds
2
+ class Object
3
+ attr_accessor :redis_key
4
+
5
+ def initialize(redis_key)
6
+ @redis_key = format_redis_key(redis_key)
7
+ end
8
+
9
+ def self.flushdb
10
+ connection.flushdb
11
+ end
12
+
13
+ @@namespace = nil
14
+ @@connection = nil
15
+ def self.configure(options)
16
+ @@namespace = options[:namespace]
17
+ @@connection = options[:connection]
18
+ end
19
+
20
+ def self.connection
21
+ return @@connection
22
+ end
23
+
24
+ def connection
25
+ return @@connection
26
+ end
27
+
28
+ def namespace
29
+ return @@namespace
30
+ end
31
+
32
+ def delete
33
+ return connection.del(@redis_key)
34
+ end
35
+
36
+ def type
37
+ return connection.type(@redis_key)
38
+ end
39
+
40
+ def expire(expiry)
41
+ return connection.expire(@redis_key, expiry)
42
+ end
43
+
44
+ def expireat(timestamp)
45
+ return connection.expireat(@redis_key, timestamp.to_i)
46
+ end
47
+
48
+ def dump
49
+ return connection.dump(@redis_key)
50
+ end
51
+
52
+ def exists?
53
+ return connection.exists(@redis_key)
54
+ end
55
+
56
+ def persist
57
+ return connection.persist(@redis_key)
58
+ end
59
+
60
+ def ttl
61
+ return connection.ttl(@redis_key)
62
+ end
63
+
64
+ def pttl
65
+ return connection.pttl(@redis_key)
66
+ end
67
+
68
+ def format_redis_key(key)
69
+ key = "#{namespace}:#{key}" unless key.start_with?(namespace)
70
+ return key
71
+ end
72
+ private :format_redis_key
73
+ end
74
+ end
@@ -0,0 +1,60 @@
1
+ module RedisRds
2
+ class Set < RedisRds::Object
3
+ include Enumerable
4
+
5
+ def add(item)
6
+ return connection.sadd(@redis_key, item)
7
+ end
8
+ alias << add
9
+ alias push add
10
+
11
+ def merge(enumerable)
12
+ lua_script = "
13
+ local result = 0;
14
+ for i, item in ipairs(ARGV) do
15
+ result = result + redis.call('sadd', KEYS[1], item);
16
+ end
17
+ return result;"
18
+ result = connection.eval(lua_script, [@redis_key], enumerable.entries)
19
+ result = [] if result.blank?
20
+
21
+ return result
22
+ end
23
+
24
+ def remove(item)
25
+ return connection.srem(@redis_key, item)
26
+ end
27
+
28
+ def all
29
+ return ::Set.new(connection.smembers(@redis_key)).to_a
30
+ end
31
+
32
+ def consume
33
+ lua_script = "
34
+ local result = redis.call('smembers', KEYS[1]);
35
+ redis.call('del', KEYS[1]);
36
+ return result;"
37
+ result = connection.eval(lua_script, [@redis_key])
38
+ result = [] if result.blank?
39
+
40
+ return ::Set.new(Array.wrap(result).compact).to_a
41
+ end
42
+
43
+ def size
44
+ return connection.scard(@redis_key)
45
+ end
46
+
47
+ def include?(object)
48
+ return connection.sismember(@redis_key, object)
49
+ end
50
+
51
+ def to_json
52
+ return all.to_json
53
+ end
54
+
55
+ # TODO: Implement lazy enumerator
56
+ def each(&block)
57
+ return all.each(&block)
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,61 @@
1
+ module RedisRds
2
+ class SortedSet < RedisRds::Object
3
+ include Enumerable
4
+
5
+ def add(score, item)
6
+ return connection.zadd(@redis_key, score, item)
7
+ end
8
+
9
+ def push(item)
10
+ index = size
11
+ return add(index, item)
12
+ end
13
+ alias << push
14
+
15
+ def all
16
+ return connection.zrange(@redis_key, 0, -1).to_a
17
+ end
18
+
19
+ def empty?
20
+ return size == 0
21
+ end
22
+
23
+ def size
24
+ return connection.zcard(@redis_key)
25
+ end
26
+
27
+ def include?(item)
28
+ return !index_of(item).nil?
29
+ end
30
+
31
+ def remove(item)
32
+ return connection.zrem(@redis_key, item)
33
+ end
34
+
35
+ def remove_by_score(min, max)
36
+ return connection.zremrangebyscore(@redis_key, min, max)
37
+ end
38
+
39
+ def range(min, max, order: :asc)
40
+ case order
41
+ when :desc
42
+ connection.zrevrange(@redis_key, min, max)
43
+ else
44
+ connection.zrange(@redis_key, min, max)
45
+ end
46
+ end
47
+
48
+ def index_of(item)
49
+ return connection.zrank(@redis_key, item)
50
+ end
51
+
52
+ def to_json
53
+ return all.to_json
54
+ end
55
+
56
+ # TODO: Implement lazy enumerator
57
+ def each(&block)
58
+ return all.each(&block)
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,30 @@
1
+ module RedisRds
2
+ class SortedStringSet < RedisRds::SortedSet
3
+ DEFAULT_SCORE = 0
4
+
5
+ def add(item)
6
+ super(DEFAULT_SCORE, item)
7
+ end
8
+
9
+ def push(item)
10
+ return add(item)
11
+ end
12
+
13
+ def all
14
+ return range('-', '+').to_a
15
+ end
16
+
17
+ def remove(item)
18
+ return connection.zrem(@redis_key, item)
19
+ end
20
+
21
+ def range(min, max)
22
+ min, max = [min, max].map do |r|
23
+ next(r) if ['-', '+'].include?(r) || r.start_with?('[') || r.start_with?('(')
24
+ "[#{r}"
25
+ end
26
+
27
+ return connection.zrangebylex(@redis_key, min, max)
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,43 @@
1
+ module RedisRds
2
+ class String < RedisRds::Object
3
+ def get
4
+ return connection.get(@redis_key)
5
+ end
6
+
7
+ def set(value)
8
+ return connection.set(@redis_key, value)
9
+ end
10
+
11
+ def setex(value, expiry)
12
+ return connection.setex(@redis_key, expiry, value)
13
+ end
14
+
15
+ def append(suffix)
16
+ return connection.append(@redis_key, suffix)
17
+ end
18
+
19
+ def incr
20
+ return connection.incr(@redis_key)
21
+ end
22
+
23
+ def incrby(increment)
24
+ return connection.incrby(@redis_key, increment)
25
+ end
26
+
27
+ def length
28
+ return connection.strlen(@redis_key)
29
+ end
30
+
31
+ def setnx(value)
32
+ return connection.setnx(@redis_key, value)
33
+ end
34
+
35
+ def decr
36
+ return connection.decr(@redis_key)
37
+ end
38
+
39
+ def decrby(decrement)
40
+ return connection.decrby(@redis_key, decrement)
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,3 @@
1
+ module RedisRds
2
+ VERSION = '0.1.1'.freeze
3
+ end
data/lib/redis_rds.rb ADDED
@@ -0,0 +1,20 @@
1
+ require 'active_support'
2
+ require 'active_support/core_ext'
3
+
4
+ require 'redis_rds/version'
5
+
6
+ require 'redis_rds/config'
7
+ require 'redis_rds/composite'
8
+ require 'redis_rds/object'
9
+ require 'redis_rds/string'
10
+ require 'redis_rds/hash'
11
+ require 'redis_rds/expirable_hash'
12
+ require 'redis_rds/list'
13
+ require 'redis_rds/mutex'
14
+ require 'redis_rds/nested_hash'
15
+ require 'redis_rds/set'
16
+ require 'redis_rds/sorted_set'
17
+ require 'redis_rds/sorted_string_set'
18
+
19
+ module RedisRds
20
+ end
data/redis_rds.gemspec ADDED
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'redis_rds/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'redis_rds'
8
+ spec.version = RedisRds::VERSION
9
+ spec.authors = ['Checkitmobile GmbH']
10
+ spec.email = ['support@barcoo.com']
11
+
12
+ spec.summary = 'Ruby data structures stored in Redis.'
13
+ spec.description = 'RedisRds provides Ruby interfaces for data structures like String or Hash stored in Redis.'
14
+ spec.homepage = 'https://github.com/barcoo/redis_rds'
15
+ spec.license = 'MIT'
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = 'exe'
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ['lib']
21
+
22
+ spec.add_dependency 'redis', '~> 3.2'
23
+ spec.add_dependency 'activesupport', '~> 4'
24
+
25
+ spec.add_development_dependency 'bundler', '~> 1.12'
26
+ spec.add_development_dependency 'rake', '~> 10.0'
27
+ spec.add_development_dependency 'minitest', '~> 5.0'
28
+ spec.add_development_dependency 'pry'
29
+ end
@@ -0,0 +1,23 @@
1
+ {
2
+ "folders":
3
+ [
4
+ {
5
+ "path": ".",
6
+ "folder_exclude_patterns": ["log", "tmp"],
7
+ "file_exclude_patterns": [".rubocop-*", "*.md-u", "*~"]
8
+ }
9
+ ],
10
+ "settings":
11
+ {
12
+ "translate_tabs_to_spaces": true
13
+ },
14
+ "SublimeLinter": {
15
+ "linters": {
16
+ "rubocop": {
17
+ "@disable": false,
18
+ "args": ["--config", "${project}/.rubocop.yml"],
19
+ "excludes": []
20
+ }
21
+ }
22
+ }
23
+ }
metadata ADDED
@@ -0,0 +1,157 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: redis_rds
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Checkitmobile GmbH
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-08-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: redis
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '4'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '4'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.12'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.12'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: minitest
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '5.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '5.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: pry
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: RedisRds provides Ruby interfaces for data structures like String or
98
+ Hash stored in Redis.
99
+ email:
100
+ - support@barcoo.com
101
+ executables: []
102
+ extensions: []
103
+ extra_rdoc_files: []
104
+ files:
105
+ - ".coveralls.yml"
106
+ - ".gitignore"
107
+ - ".rubocop.yml"
108
+ - ".travis.yml"
109
+ - CHANGELOG.md
110
+ - Gemfile
111
+ - LICENSE.txt
112
+ - README.md
113
+ - Rakefile
114
+ - bin/console
115
+ - bin/setup
116
+ - doc/ROADMAP.md
117
+ - lib/redis_rds.rb
118
+ - lib/redis_rds/composite.rb
119
+ - lib/redis_rds/config.rb
120
+ - lib/redis_rds/expirable_hash.rb
121
+ - lib/redis_rds/hash.rb
122
+ - lib/redis_rds/list.rb
123
+ - lib/redis_rds/mutex.rb
124
+ - lib/redis_rds/nested_hash.rb
125
+ - lib/redis_rds/object.rb
126
+ - lib/redis_rds/set.rb
127
+ - lib/redis_rds/sorted_set.rb
128
+ - lib/redis_rds/sorted_string_set.rb
129
+ - lib/redis_rds/string.rb
130
+ - lib/redis_rds/version.rb
131
+ - redis_rds.gemspec
132
+ - redis_rds.sublime-project
133
+ homepage: https://github.com/barcoo/redis_rds
134
+ licenses:
135
+ - MIT
136
+ metadata: {}
137
+ post_install_message:
138
+ rdoc_options: []
139
+ require_paths:
140
+ - lib
141
+ required_ruby_version: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ required_rubygems_version: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - ">="
149
+ - !ruby/object:Gem::Version
150
+ version: '0'
151
+ requirements: []
152
+ rubyforge_project:
153
+ rubygems_version: 2.6.6
154
+ signing_key:
155
+ specification_version: 4
156
+ summary: Ruby data structures stored in Redis.
157
+ test_files: []