Last active
August 29, 2015 14:20
-
-
Save opsb/d1e9882b9be88af6d9f8 to your computer and use it in GitHub Desktop.
ActiveRecordOrderedSet for redis
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| class Redis::ActiveRecordOrderedSet | |
| include Enumerable | |
| delegate :each, :to => :entries | |
| def initialize(name, options={}) | |
| @set_name = name | |
| @data_set = Redis::SortedSet.new(@set_name) | |
| @updated_at_set = Redis::HashKey.new("meta_sorted_set::updated_at") | |
| @storage_limit = options[:storage_limit] | |
| end | |
| def add(record) | |
| @data_set.add(key_for_record(record), Time.zone.now.to_i) | |
| prune! | |
| touch! | |
| end | |
| alias_method :<<, :add | |
| def delete(record) | |
| @data_set.delete(key_for_record(record)) | |
| end | |
| def updated_at | |
| time = @updated_at_set[@set_name] | |
| time && Time.at(time.to_i) | |
| end | |
| def length | |
| @data_set.length | |
| end | |
| alias_method :size, :length | |
| alias_method :count, :length | |
| def top(count) | |
| @data_set.revrange(0, count-1).map do |key| | |
| record_for_key(key) | |
| end.compact | |
| end | |
| def [](index) | |
| key = @data_set.revrange(index, index).first | |
| key && record_for_key(key) | |
| end | |
| def touch! | |
| @updated_at_set[@set_name] = Time.zone.now.to_i | |
| end | |
| def count | |
| @data_set.length | |
| end | |
| private | |
| def entries | |
| keys = @data_set.members.reverse | |
| Content.children_for_keys(keys).compact | |
| end | |
| def key_for_record(record) | |
| raise "Record was null" unless record | |
| "#{record.class.to_s.underscore}-#{record.id}" | |
| end | |
| def record_for_key(key) | |
| class_name, id = key.split("-") | |
| record = class_name.singularize.classify.constantize.find_by_id(id) | |
| end | |
| def prune! | |
| return unless @storage_limit | |
| if count > @storage_limit | |
| @data_set.remrangebyrank(0, count - 1 - @storage_limit) | |
| end | |
| end | |
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| require 'spec_helper' | |
| describe Redis::ActiveRecordOrderedSet do | |
| describe "empty set", unscoped: true do | |
| let(:active_record_ordered_set){ Redis::ActiveRecordOrderedSet.new("tenant-test-trending-content") } | |
| let(:post){ FactoryGirl.create(:post) } | |
| let(:index){ 1 } | |
| context "after adding an item" do | |
| let(:now){ Time.now } | |
| before do | |
| Timecop.freeze(now) | |
| active_record_ordered_set.add(post) | |
| end | |
| it "should have set updated_at" do | |
| active_record_ordered_set.updated_at.to_i.should eq now.to_i | |
| end | |
| it "should include post in top items" do | |
| active_record_ordered_set.top(1).first.should == post | |
| end | |
| it "should include the item during iteration" do | |
| items = [] | |
| active_record_ordered_set.each do |item| | |
| items << item | |
| end | |
| items.should include(post) | |
| end | |
| end | |
| end | |
| describe "limited to 5 entries", unscoped: true do | |
| let(:limit){ 5 } | |
| let(:active_record_ordered_set){ Redis::ActiveRecordOrderedSet.new("tenant-test-trending-content", storage_limit: limit) } | |
| let(:items){ (1..10).map{ |n| FactoryGirl.create(:post) } } | |
| context "after adding 10 items" do | |
| before do | |
| 0.upto(9) do |n| | |
| Timecop.freeze( n.minute.from_now ) do | |
| active_record_ordered_set.add(items[n]) | |
| end | |
| end | |
| end | |
| it "should only contain 5 items" do | |
| active_record_ordered_set.count.should == 5 | |
| end | |
| it "should have kept the 5 newest items" do | |
| active_record_ordered_set.top(5).should == items[5..9].reverse | |
| end | |
| it "should include all posts for #to_a" do | |
| active_record_ordered_set.to_a.should == items[5..9].reverse | |
| end | |
| end | |
| end | |
| end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment