ohm-sorted 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ NDlkNzNmNzllNTJmMDEzODExNGQ5MDlhMWM5NTYyMjA5MDQwNGUwMA==
5
+ data.tar.gz: !binary |-
6
+ ZjQ5YzdhNTg2NTYzMzY0YjljZjcxZDQ3Y2JjZjJhMmUzYWFmZmZlNw==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ MmFmNjgzODkwNzMyYzRiMjNhZTM2MDI5N2Y4NjRhYzMyMzcyNDgxZTYzMWE2
10
+ ZGZmNjI5M2E1ODdjYTE1MGYzMjlkZGFmYTI1MDE0ZDY1OTEyZTIyYzVhNzg4
11
+ OTk3NWVkMmFkNjE5M2UwNDdmZTUzYWE4YTE1MGUxNGJmMDg2MmM=
12
+ data.tar.gz: !binary |-
13
+ MzM3NDc5YWQzN2ZiN2EzOWQ3OTIxZDAxMDI5Y2YzZTEyMDlhMzcyNjVjMDdi
14
+ Yzc3NTQ4ZTQ3NzA2ZmI5NmIyZjk1NDIxNjYwZjg4NGU0NTAzM2JlNTljNTE0
15
+ ZWQ5ZGZjZDkwNmQyNmFhMjRhMDJjNTlmMjY3MmIxN2JiMDdmZWE=
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ /pkg
2
+ /doc
3
+ /test/db/*
4
+ gemfiles
5
+ Gemfile.lock
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ before_script: "rake appraisal:install"
3
+ script: "rake appraisal"
data/Appraisals ADDED
@@ -0,0 +1,11 @@
1
+ appraise "ohm-0.x" do
2
+ gem "redis", "2.2.2"
3
+ gem "ohm", "0.1.3"
4
+ gem "ohm-contrib", "0.1.2"
5
+ end
6
+
7
+ appraise "ohm-1.x" do
8
+ gem "redis", "3.0.4"
9
+ gem "ohm", "1.3.2"
10
+ gem "ohm-contrib", "1.2"
11
+ end
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ ### 0.1.0
2
+
3
+ - Initial release
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "appraisal"
4
+ gem "ohm"
5
+ gem "ohm-contrib"
data/README.md ADDED
@@ -0,0 +1,41 @@
1
+ ohm-sorted
2
+ ==========
3
+
4
+ Sorted indexes for Ohm
5
+
6
+
7
+ Setup
8
+ -----
9
+
10
+ 1. Include the `Callbacks` and `Sorted` modules in your model:
11
+
12
+ include Ohm::Callbacks
13
+ include Ohm::Sorted
14
+
15
+ 2. Add a sorted index to your model with the following line:
16
+
17
+ sorted :status, by: :ranking
18
+
19
+ You will need to resave every model if they already exist.
20
+
21
+ Usage
22
+ -----
23
+
24
+ To query the sorted index, use the `sorted_find` class method.
25
+
26
+ >> Post.sorted_find(:ranking, status: "draft")
27
+
28
+ This returns an Ohm::SortedSet, which is just a subclass of Ohm::BasicSet
29
+ backed by a sorted set.
30
+
31
+
32
+ Requirements
33
+ ------------
34
+
35
+ This plugin works with Ohm versions higher than 0.1.3.
36
+
37
+
38
+ Acknowledgements
39
+ ----------------
40
+
41
+ Many thanks to Damian Janowski (https://github.com/djanowski)
data/Rakefile ADDED
@@ -0,0 +1,33 @@
1
+ require "rake/testtask"
2
+ require "appraisal"
3
+ require "bundler/setup"
4
+
5
+ REDIS_DIR = File.expand_path(File.join("..", "test"), __FILE__)
6
+ REDIS_CNF = File.join(REDIS_DIR, "test.conf")
7
+ REDIS_PID = File.join(REDIS_DIR, "db", "redis.pid")
8
+
9
+ task :default => :run
10
+
11
+ desc "Run tests and manage server start/stop"
12
+ task :run => [:start, :test, :stop]
13
+
14
+ desc "Start the Redis server"
15
+ task :start do
16
+ unless File.exists?(REDIS_PID)
17
+ system "redis-server #{REDIS_CNF}"
18
+ end
19
+ end
20
+
21
+ desc "Stop the Redis server"
22
+ task :stop do
23
+ if File.exists?(REDIS_PID)
24
+ system "kill #{File.read(REDIS_PID)}"
25
+ File.delete(REDIS_PID)
26
+ end
27
+ end
28
+
29
+ Rake::TestTask.new do |t|
30
+ t.libs << "test"
31
+ t.test_files = FileList['test/*_test.rb']
32
+ t.verbose = true
33
+ end
data/UNLICENSE ADDED
@@ -0,0 +1,24 @@
1
+ This is free and unencumbered software released into the public domain.
2
+
3
+ Anyone is free to copy, modify, publish, use, compile, sell, or
4
+ distribute this software, either in source code form or as a compiled
5
+ binary, for any purpose, commercial or non-commercial, and by any
6
+ means.
7
+
8
+ In jurisdictions that recognize copyright laws, the author or authors
9
+ of this software dedicate any and all copyright interest in the
10
+ software to the public domain. We make this dedication for the benefit
11
+ of the public at large and to the detriment of our heirs and
12
+ successors. We intend this dedication to be an overt act of
13
+ relinquishment in perpetuity of all present and future rights to this
14
+ software under copyright law.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20
+ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ For more information, please refer to <http://unlicense.org/>
data/lib/ohm/sorted.rb ADDED
@@ -0,0 +1,163 @@
1
+ require 'ohm'
2
+ require 'ohm/contrib'
3
+
4
+ module Ohm
5
+
6
+ if defined?(BasicSet)
7
+ class SortedSet < BasicSet
8
+ attr :key
9
+ attr :namespace
10
+ attr :model
11
+
12
+ def initialize(key, namespace, model)
13
+ @key = key
14
+ @namespace = namespace
15
+ @model = model
16
+ end
17
+
18
+ def ids
19
+ execute { |key| db.zrange(key, 0, -1) }
20
+ end
21
+
22
+ def size
23
+ execute { |key| db.zcard(key) }
24
+ end
25
+
26
+ private
27
+ def exists?(id)
28
+ execute { |key| !!db.zscore(key, id) }
29
+ end
30
+
31
+ def execute
32
+ yield key
33
+ end
34
+
35
+ def db
36
+ model.db
37
+ end
38
+ end
39
+ else
40
+ class SortedSet < Model::Collection
41
+ attr :key
42
+ attr :model
43
+
44
+ def initialize(key, _, model)
45
+ @key = key
46
+ @model = model
47
+ end
48
+
49
+ def db
50
+ model.db
51
+ end
52
+
53
+ def each(&block)
54
+ db.zrange(key, 0, -1).each { |id| block.call(model.to_proc[id]) }
55
+ end
56
+
57
+ def [](id)
58
+ model[id] if !!db.zrank(key, id)
59
+ end
60
+
61
+ def size
62
+ db.zcard(key)
63
+ end
64
+
65
+ def all
66
+ db.zrange(key, 0, -1).map(&model)
67
+ end
68
+
69
+ def first
70
+ db.zrange(key, 0, 1).map(&model).first
71
+ end
72
+
73
+ def include?(model)
74
+ !!db.zrank(key, model.id)
75
+ end
76
+
77
+ def inspect
78
+ "#<SortedSet (#{model}): #{db.zrange(key, 0, -1).inspect}>"
79
+ end
80
+ end
81
+ end
82
+
83
+ module Sorted
84
+ def self.included(model)
85
+ model.extend(ClassMethods)
86
+ end
87
+
88
+ module ClassMethods
89
+ def sorted(attr, options={})
90
+ sorted_indices[attr] = options
91
+ end
92
+
93
+ def sorted_indices
94
+ @sorted_indices ||= {}
95
+ end
96
+
97
+ def sorted_find(attribute, dict)
98
+ unless sorted_index_exists?(dict.keys.first, by: attribute)
99
+ raise index_not_found(attribute)
100
+ end
101
+
102
+ index_key = sorted_index_key(attribute, dict)
103
+ Ohm::SortedSet.new(index_key, key, self)
104
+ end
105
+
106
+ def sorted_index_exists?(attribute, options=nil)
107
+ index = sorted_indices[attribute]
108
+ !!(index && (options.nil? || options == index))
109
+ end
110
+
111
+ def sorted_index_key(attribute, dict)
112
+ [key, "sorted", dict.keys.first, attribute, dict.values.first].join(":")
113
+ end
114
+
115
+ protected
116
+ def index_not_found(attribute)
117
+ if defined?(IndexNotFound)
118
+ IndexNotFound
119
+ else
120
+ Model::IndexNotFound.new(attribute)
121
+ end
122
+ end
123
+ end
124
+
125
+ protected
126
+ def after_create
127
+ add_sorted_indices
128
+ super
129
+ end
130
+
131
+ def after_update
132
+ add_sorted_indices unless new?
133
+ super
134
+ end
135
+
136
+ def before_delete
137
+ remove_sorted_indices
138
+ super
139
+ end
140
+
141
+ def add_sorted_indices
142
+ update_sorted_indices do |key, attribute, options|
143
+ score = send(options[:by]).to_f
144
+ db.zadd(key, score, id)
145
+ end
146
+ end
147
+
148
+ def remove_sorted_indices
149
+ update_sorted_indices do |key, attribute, options|
150
+ db.zrem(key, id)
151
+ end
152
+ end
153
+
154
+ def update_sorted_indices
155
+ self.class.sorted_indices.each do |args|
156
+ attribute, options = *args
157
+ key = self.class.sorted_index_key(
158
+ options[:by], {attribute => send(attribute)})
159
+ yield(key, attribute, options)
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,11 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'ohm-sorted'
3
+ s.version = '0.1.0'
4
+ s.summary = "Sorted indices for Ohm."
5
+ s.description = "An plugin for Ohm that lets you create sorted indices."
6
+ s.author = "Federico Bond"
7
+ s.email = 'federico@educabilia.com'
8
+ s.files = `git ls-files`.split("\n")
9
+ s.homepage = 'https://github.com/educabilia/ohm-sorted'
10
+ s.license = 'UNLICENSE'
11
+ end
@@ -0,0 +1,93 @@
1
+ require 'test/unit'
2
+ require 'ohm/sorted'
3
+
4
+ class Post < Ohm::Model
5
+ include Ohm::Callbacks
6
+ include Ohm::Sorted
7
+
8
+ attribute :order
9
+ attribute :status
10
+
11
+ sorted :status, by: :order
12
+ end
13
+
14
+ class SortedTest < Test::Unit::TestCase
15
+ def setup
16
+ Ohm.flush
17
+ end
18
+
19
+ def test_sorted_find_returns_sorted_set
20
+ Post.create(status: "draft", order: 1)
21
+ sorted_set = Post.sorted_find(:order, status: "draft")
22
+ assert_equal Ohm::SortedSet, sorted_set.class
23
+ assert_equal "Post:sorted:status:order:draft", sorted_set.key
24
+ end
25
+
26
+ def test_sorted_find_first
27
+ post = Post.create(status: "draft", order: 1)
28
+ sorted_set = Post.sorted_find(:order, status: "draft")
29
+
30
+ assert_equal post, sorted_set.first
31
+ end
32
+
33
+ def test_sorted_find_order
34
+ post_1 = Post.create(status: "draft", order: 2)
35
+ post_2 = Post.create(status: "draft", order: 3)
36
+ post_3 = Post.create(status: "draft", order: 1)
37
+ sorted_set = Post.sorted_find(:order, status: "draft")
38
+
39
+ assert_equal [post_3, post_1, post_2], sorted_set.to_a
40
+ end
41
+
42
+ def test_update
43
+ post_1 = Post.create(status: "draft", order: 1)
44
+ post_2 = Post.create(status: "draft", order: 2)
45
+
46
+ sorted_set = Post.sorted_find(:order, status: "draft")
47
+ assert_equal [post_1, post_2], sorted_set.to_a
48
+
49
+ post_1.update(order: 3)
50
+
51
+ sorted_set = Post.sorted_find(:order, status: "draft")
52
+ assert_equal [post_2, post_1], sorted_set.to_a
53
+ end
54
+
55
+ def test_delete
56
+ post = Post.create(status: "draft", order: 1)
57
+ post.delete
58
+
59
+ sorted_set = Post.sorted_find(:order, status: "draft")
60
+ assert_equal [], sorted_set.to_a
61
+ end
62
+
63
+ def test_indexes_nil
64
+ post = Post.create(status: "draft")
65
+ sorted_set = Post.sorted_find(:order, status: "draft")
66
+ assert_equal [post], sorted_set.to_a
67
+ end
68
+
69
+ def test_sorted_find_invalid
70
+ exception_class = defined?(Ohm::IndexNotFound) ? Ohm::IndexNotFound : Ohm::Model::IndexNotFound
71
+
72
+ Post.create(status: "draft", order: 1)
73
+ assert_raises(exception_class) do
74
+ Post.sorted_find(:foo, status: "draft")
75
+ end
76
+
77
+ assert_raises(exception_class) do
78
+ Post.sorted_find(:order, foo: "bar")
79
+ end
80
+ end
81
+
82
+ def test_sorted_set_index
83
+ post = Post.create(status: "draft", order: 1)
84
+ sorted_set = Post.sorted_find(:order, status: "draft")
85
+ assert_equal post, sorted_set[post.id]
86
+ end
87
+
88
+ def test_sorted_set_size
89
+ Post.create(status: "draft", order: 1)
90
+ sorted_set = Post.sorted_find(:order, status: "draft")
91
+ assert_equal 1, sorted_set.size
92
+ end
93
+ end
data/test/test.conf ADDED
@@ -0,0 +1,8 @@
1
+ dir ./test/db
2
+ pidfile ./redis.pid
3
+ port 6379
4
+ timeout 300
5
+ loglevel debug
6
+ logfile stdout
7
+ databases 16
8
+ daemonize yes
metadata ADDED
@@ -0,0 +1,55 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ohm-sorted
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Federico Bond
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-09-05 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: An plugin for Ohm that lets you create sorted indices.
14
+ email: federico@educabilia.com
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - .gitignore
20
+ - .travis.yml
21
+ - Appraisals
22
+ - CHANGELOG.md
23
+ - Gemfile
24
+ - README.md
25
+ - Rakefile
26
+ - UNLICENSE
27
+ - lib/ohm/sorted.rb
28
+ - ohm-sorted.gemspec
29
+ - test/sorted_test.rb
30
+ - test/test.conf
31
+ homepage: https://github.com/educabilia/ohm-sorted
32
+ licenses:
33
+ - UNLICENSE
34
+ metadata: {}
35
+ post_install_message:
36
+ rdoc_options: []
37
+ require_paths:
38
+ - lib
39
+ required_ruby_version: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ required_rubygems_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ! '>='
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ requirements: []
50
+ rubyforge_project:
51
+ rubygems_version: 2.0.7
52
+ signing_key:
53
+ specification_version: 4
54
+ summary: Sorted indices for Ohm.
55
+ test_files: []