ohm-sorted 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 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: []