seymour 0.0.7 → 0.0.8
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.
- data/.gitignore +2 -0
- data/.travis.yml +7 -0
- data/CHANGELOG.md +14 -0
- data/Gemfile +13 -15
- data/Gemfile.ci +4 -0
- data/README.md +3 -0
- data/lib/seymour.rb +2 -0
- data/lib/seymour/acts_as_activity.rb +0 -2
- data/lib/seymour/feed.rb +20 -56
- data/lib/seymour/railtie.rb +20 -0
- data/lib/seymour/redis.rb +11 -6
- data/lib/seymour/store.rb +7 -0
- data/lib/seymour/store/base.rb +35 -0
- data/lib/seymour/store/list.rb +104 -0
- data/lib/seymour/store/zset.rb +75 -0
- data/lib/{tasks/seymour_tasks.rake → seymour/tasks/seymour_tasks.rb} +0 -0
- data/lib/seymour/version.rb +1 -1
- data/seymour.gemspec +6 -8
- data/spec/dummy/db/schema.rb +3 -2
- data/spec/seymour/distributable_spec.rb +13 -6
- data/spec/seymour/feed_spec.rb +266 -76
- metadata +87 -159
- data/Gemfile.lock +0 -195
data/.gitignore
CHANGED
data/.travis.yml
ADDED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
## 0.0.8 (2012-2-06)
|
2
|
+
|
3
|
+
* travis build status; clean Gemfile dependencies
|
4
|
+
* Move rails initialization to railtie
|
5
|
+
* Added support for zunionstore and zinterstore
|
6
|
+
* extract redis list object from feed
|
7
|
+
* feed class method 'key' to define key override
|
8
|
+
|
9
|
+
## 0.0.7 (2011-12-04)
|
10
|
+
|
11
|
+
* Support for zset feeds
|
12
|
+
* Extract redis methods to store module
|
13
|
+
* Rails generator for basic feed
|
14
|
+
|
1
15
|
## 0.0.6 (2011-10-24)
|
2
16
|
|
3
17
|
* Ruby 1.8.7 compatiblity
|
data/Gemfile
CHANGED
@@ -1,23 +1,21 @@
|
|
1
|
-
source
|
1
|
+
source :rubygems
|
2
2
|
|
3
|
-
# Declare your gem's dependencies in seymour.gemspec.
|
4
|
-
# Bundler will treat runtime dependencies like base dependencies, and
|
5
|
-
# development dependencies will be added by default to the :development group.
|
6
3
|
gemspec
|
4
|
+
gem 'redis-namespace', :git => "git://github.com/defunkt/redis-namespace.git"
|
7
5
|
|
8
6
|
# Declare any dependencies that are still in development here instead of in
|
9
7
|
# your gemspec. These might include edge Rails or gems from your path or
|
10
8
|
# Git. Remember to move these dependencies to your gemspec before releasing
|
11
9
|
# your gem to rubygems.org.
|
12
10
|
|
13
|
-
# To use debugger
|
14
|
-
group :
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
end
|
11
|
+
# # To use debugger
|
12
|
+
# group :development do
|
13
|
+
# gem 'ruby-debug19', :require => 'ruby-debug', :platform => :ruby_19
|
14
|
+
# gem 'ruby-debug', :platform => :ruby_18
|
15
|
+
# gem 'guard'
|
16
|
+
# gem 'guard-rspec'
|
17
|
+
# gem 'rb-fsevent' # Makes guard better
|
18
|
+
# gem 'growl_notify' # Makes guard better
|
19
|
+
# gem 'launchy'
|
20
|
+
# gem 'yard'
|
21
|
+
# end
|
data/Gemfile.ci
ADDED
data/README.md
CHANGED
@@ -5,6 +5,9 @@ Feed me activities, Seymour, please!
|
|
5
5
|
Seymour is a library for distributing activity items to Redis-backed activity feeds
|
6
6
|
in a Rails application.
|
7
7
|
|
8
|
+
[](http://travis-ci.org/rossta/seymour)
|
9
|
+
|
10
|
+
|
8
11
|
## Install
|
9
12
|
|
10
13
|
In your Gemfile
|
data/lib/seymour.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
require "seymour/version"
|
2
2
|
require "seymour/redis"
|
3
|
+
require "seymour/store"
|
3
4
|
require "seymour/feed"
|
4
5
|
require "seymour/acts_as_activity"
|
5
6
|
require "seymour/distributable"
|
6
7
|
require "seymour/renderable"
|
7
8
|
require "seymour/render_controller"
|
9
|
+
require 'seymour/railtie' if defined?(Rails)
|
8
10
|
|
9
11
|
module Seymour
|
10
12
|
extend self
|
data/lib/seymour/feed.rb
CHANGED
@@ -4,79 +4,55 @@ module Seymour
|
|
4
4
|
attr_accessor :owner
|
5
5
|
|
6
6
|
class << self
|
7
|
-
|
7
|
+
|
8
|
+
def key(&block)
|
9
|
+
define_method('key_to_store', &block)
|
10
|
+
end
|
8
11
|
|
9
12
|
def distribute(activity)
|
10
13
|
activity.distribute
|
11
14
|
end
|
12
15
|
|
13
16
|
def inherited(subclass)
|
14
|
-
|
17
|
+
feed_classes << subclass
|
15
18
|
end
|
16
19
|
|
17
20
|
def feed_classes
|
18
|
-
@@feed_classes
|
21
|
+
@@feed_classes ||= []
|
19
22
|
end
|
20
|
-
end
|
21
|
-
|
22
|
-
def initialize(owner)
|
23
|
-
@owner = owner
|
24
|
-
end
|
25
23
|
|
26
|
-
|
27
|
-
|
28
|
-
end
|
29
|
-
|
30
|
-
def push(activity)
|
31
|
-
perform_push(activity.id) if should_push?(activity)
|
32
|
-
end
|
33
|
-
|
34
|
-
def bulk_push(activities)
|
35
|
-
activities.each do |activity|
|
36
|
-
push(activity)
|
24
|
+
def store(store_type)
|
25
|
+
@store_type = "seymour/store/#{store_type}".camelize.constantize
|
37
26
|
end
|
38
|
-
end
|
39
27
|
|
40
|
-
|
41
|
-
|
28
|
+
def store_type
|
29
|
+
@store_type ||= Seymour::Store::List
|
30
|
+
end
|
42
31
|
end
|
43
32
|
|
44
|
-
def
|
45
|
-
|
33
|
+
def initialize(owner)
|
34
|
+
@owner = owner
|
46
35
|
end
|
47
36
|
|
48
|
-
|
49
|
-
|
50
|
-
end
|
37
|
+
delegate :key, :key=, :push, :sort, :sort!, :insert_and_order, :bulk_push,
|
38
|
+
:remove, :remove_id, :remove_all, :ids, :activity_ids, :to => :store
|
51
39
|
|
52
|
-
|
53
|
-
redis.sort(key, options)
|
54
|
-
end
|
40
|
+
delegate :union, :intersect, :to => :store
|
55
41
|
|
56
|
-
def
|
57
|
-
|
58
|
-
sort!
|
42
|
+
def store
|
43
|
+
@store ||= self.class.store_type.new(key_to_store)
|
59
44
|
end
|
60
|
-
alias_method :insert_and_order, :sorted_push
|
61
45
|
|
62
46
|
protected
|
63
47
|
|
64
|
-
def
|
65
|
-
|
66
|
-
end
|
67
|
-
|
68
|
-
def remove_all
|
69
|
-
redis.del(key)
|
48
|
+
def key_to_store
|
49
|
+
"#{owner_name}:#{id_for_key}/#{feed_name}"
|
70
50
|
end
|
71
51
|
|
72
52
|
def owner_name
|
73
53
|
owner.class.name
|
74
54
|
end
|
75
55
|
|
76
|
-
def key
|
77
|
-
"#{owner.class.name}:#{id_for_key}/#{feed_name}"
|
78
|
-
end
|
79
|
-
|
80
56
|
def id_for_key
|
81
57
|
owner.id
|
82
58
|
end
|
@@ -85,17 +61,5 @@ module Seymour
|
|
85
61
|
self.class.name.downcase
|
86
62
|
end
|
87
63
|
|
88
|
-
def max_size
|
89
|
-
100
|
90
|
-
end
|
91
|
-
|
92
|
-
def should_push?(activity)
|
93
|
-
!activity_ids.include?(activity.id)
|
94
|
-
end
|
95
|
-
|
96
|
-
def perform_push(id)
|
97
|
-
redis.lpush(key, id)
|
98
|
-
redis.ltrim(key, 0, max_size)
|
99
|
-
end
|
100
64
|
end
|
101
65
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Seymour
|
2
|
+
class Railtie < Rails::Railtie
|
3
|
+
config.app_generators.integration_tool :rspec
|
4
|
+
config.app_generators.test_framework :rspec
|
5
|
+
|
6
|
+
generators do
|
7
|
+
require 'generators/seymour/feed/feed_generator'
|
8
|
+
end
|
9
|
+
|
10
|
+
rake_tasks do
|
11
|
+
require 'seymour/tasks/seymour_tasks'
|
12
|
+
end
|
13
|
+
|
14
|
+
initializer "seymour.acts_as_activity" do
|
15
|
+
ActiveSupport.on_load :active_record do
|
16
|
+
include ActsAsActivity
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/seymour/redis.rb
CHANGED
@@ -1,12 +1,17 @@
|
|
1
1
|
require 'redis-namespace'
|
2
2
|
|
3
3
|
module Seymour
|
4
|
+
|
5
|
+
module Redis
|
6
|
+
|
7
|
+
end
|
8
|
+
|
4
9
|
## Courtesy of resque
|
5
10
|
# Returns the current Redis connection. If none has been created, will
|
6
11
|
# create a new one.
|
7
12
|
def redis
|
8
13
|
return @redis if @redis
|
9
|
-
self.redis = Redis.respond_to?(:connect) ? Redis.connect : "localhost:6379"
|
14
|
+
self.redis = ::Redis.respond_to?(:connect) ? ::Redis.connect : "localhost:6379"
|
10
15
|
self.redis
|
11
16
|
end
|
12
17
|
|
@@ -21,20 +26,20 @@ module Seymour
|
|
21
26
|
case server
|
22
27
|
when String
|
23
28
|
if server =~ /redis\:\/\//
|
24
|
-
redis = Redis.connect(:url => server, :thread_safe => true)
|
29
|
+
redis = ::Redis.connect(:url => server, :thread_safe => true)
|
25
30
|
else
|
26
31
|
server, namespace = server.split('/', 2)
|
27
32
|
host, port, db = server.split(':')
|
28
|
-
redis = Redis.new(:host => host, :port => port,
|
33
|
+
redis = ::Redis.new(:host => host, :port => port,
|
29
34
|
:thread_safe => true, :db => db)
|
30
35
|
end
|
31
36
|
namespace ||= :seymour
|
32
37
|
|
33
|
-
@redis = Redis::Namespace.new(namespace, :redis => redis)
|
34
|
-
when Redis::Namespace
|
38
|
+
@redis = ::Redis::Namespace.new(namespace, :redis => redis)
|
39
|
+
when ::Redis::Namespace
|
35
40
|
@redis = server
|
36
41
|
else
|
37
|
-
@redis = Redis::Namespace.new(:seymour, :redis => server)
|
42
|
+
@redis = ::Redis::Namespace.new(:seymour, :redis => server)
|
38
43
|
end
|
39
44
|
end
|
40
45
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Seymour
|
2
|
+
module Store
|
3
|
+
class Base
|
4
|
+
attr_accessor :key
|
5
|
+
|
6
|
+
def initialize(key, options = {})
|
7
|
+
@key = key
|
8
|
+
@options = options
|
9
|
+
end
|
10
|
+
|
11
|
+
def redis
|
12
|
+
@redis ||= Seymour.redis
|
13
|
+
end
|
14
|
+
|
15
|
+
def activity_ids
|
16
|
+
ids
|
17
|
+
end
|
18
|
+
|
19
|
+
def max_size
|
20
|
+
100
|
21
|
+
end
|
22
|
+
|
23
|
+
def bulk_push(activities)
|
24
|
+
activities.each do |activity|
|
25
|
+
push(activity)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def remove(activity)
|
30
|
+
remove_id(activity.id)
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
module Seymour
|
2
|
+
module Store
|
3
|
+
class List < Seymour::Store::Base
|
4
|
+
|
5
|
+
def ids
|
6
|
+
redis.lrange(key, 0, max_size).map(&:to_i)
|
7
|
+
end
|
8
|
+
|
9
|
+
def push(activity)
|
10
|
+
perform_push(activity.id) if should_push?(activity)
|
11
|
+
end
|
12
|
+
|
13
|
+
def remove_id(id)
|
14
|
+
redis.lrem(key, 0, id)
|
15
|
+
end
|
16
|
+
|
17
|
+
def remove_all
|
18
|
+
redis.del(key)
|
19
|
+
end
|
20
|
+
|
21
|
+
def sort!(options = {})
|
22
|
+
sort({ :order => "DESC", :store => key }.merge(options)) # replaces itself with sorted list
|
23
|
+
end
|
24
|
+
|
25
|
+
def sort(options = {})
|
26
|
+
redis.sort(key, options)
|
27
|
+
end
|
28
|
+
|
29
|
+
def sorted_push(activities)
|
30
|
+
bulk_push(activities)
|
31
|
+
sort!
|
32
|
+
end
|
33
|
+
alias_method :insert_and_order, :sorted_push
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def perform_push(id)
|
38
|
+
redis.lpush(key, id)
|
39
|
+
redis.ltrim(key, 0, max_size)
|
40
|
+
end
|
41
|
+
|
42
|
+
def should_push?(activity)
|
43
|
+
!activity_ids.include?(activity.id)
|
44
|
+
end
|
45
|
+
|
46
|
+
# def llen
|
47
|
+
# redis.llen(@key)
|
48
|
+
# end
|
49
|
+
#
|
50
|
+
# def rpop
|
51
|
+
# redis.rpop(@key)
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
# def rpoplpush(destination)
|
55
|
+
# if destination.is_a?(RedisList)
|
56
|
+
# redis.rpoplpush(@key, destination.key)
|
57
|
+
# destination.trim_to_max_size
|
58
|
+
# else
|
59
|
+
# redis.rpoplpush(@key, destination)
|
60
|
+
# end
|
61
|
+
# end
|
62
|
+
#
|
63
|
+
# def lpush(val)
|
64
|
+
# redis.lpush(@key, val)
|
65
|
+
# trim_to_max_size
|
66
|
+
# end
|
67
|
+
#
|
68
|
+
# def push_multi(elements)
|
69
|
+
# redis.pipelined do
|
70
|
+
# elements.each { |element| redis.rpush(@key, element) }
|
71
|
+
# end
|
72
|
+
# end
|
73
|
+
#
|
74
|
+
# def lrange(start_index, end_index)
|
75
|
+
# arr = redis.lrange(@key, start_index, end_index)
|
76
|
+
# typecast arr
|
77
|
+
# end
|
78
|
+
#
|
79
|
+
# def all
|
80
|
+
# lrange(0, -1)
|
81
|
+
# end
|
82
|
+
#
|
83
|
+
# def ltrim(start_index, end_index)
|
84
|
+
# redis.ltrim(@key, start_index, end_index)
|
85
|
+
# rescue => ex
|
86
|
+
# raise unless ex.message =~ /no such key/
|
87
|
+
# end
|
88
|
+
#
|
89
|
+
# def trim_to_max_size
|
90
|
+
# if max_size
|
91
|
+
# redis.ltrim(@key, 0, max_size - 1)
|
92
|
+
# end
|
93
|
+
# end
|
94
|
+
#
|
95
|
+
# def lrem(value, count = 1)
|
96
|
+
# redis.lrem(@key, count, value)
|
97
|
+
# end
|
98
|
+
#
|
99
|
+
# def max_size
|
100
|
+
# @options[:max_size]
|
101
|
+
# end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module Seymour
|
2
|
+
module Store
|
3
|
+
class Zset < Seymour::Store::Base
|
4
|
+
|
5
|
+
def push(activity, score = activity.score)
|
6
|
+
redis.zadd(key, score, activity.id)
|
7
|
+
end
|
8
|
+
|
9
|
+
def ids
|
10
|
+
redis.zrevrange(key, 0, max_size).map(&:to_i)
|
11
|
+
end
|
12
|
+
|
13
|
+
def remove_id(id)
|
14
|
+
redis.zrem(key, id)
|
15
|
+
end
|
16
|
+
|
17
|
+
def union_keys(keys, options = {})
|
18
|
+
redis.zunionstore key, keys, options
|
19
|
+
end
|
20
|
+
|
21
|
+
def union(feeds, options = { :exclude_options => true })
|
22
|
+
redis.zunionstore key, feeds.map(&:key), options
|
23
|
+
end
|
24
|
+
|
25
|
+
def intersect(feeds, options = {})
|
26
|
+
redis.zinterstore key, feeds.map(&:key), options
|
27
|
+
end
|
28
|
+
|
29
|
+
# def zadd(score, member)
|
30
|
+
# redis.zadd(@key, score, member)
|
31
|
+
# end
|
32
|
+
|
33
|
+
# def zrange(range_start, range_end)
|
34
|
+
# typecast redis.zrange(@key, range_start, range_end)
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# def zrem(member)
|
38
|
+
# redis.zrem(@key, member)
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# def zincrby(increment, member)
|
42
|
+
# redis.zincrby(@key, increment, member)
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# def zincr(member)
|
46
|
+
# zincrby(1, member)
|
47
|
+
# end
|
48
|
+
#
|
49
|
+
# def zrevrange(range_start, range_end)
|
50
|
+
# typecast redis.zrevrange(@key, range_start, range_end)
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# def zrangebyscore(min, max)
|
54
|
+
# typecast redis.zrangebyscore(@key, min, max)
|
55
|
+
# end
|
56
|
+
#
|
57
|
+
# def zcard
|
58
|
+
# redis.zcard(@key)
|
59
|
+
# end
|
60
|
+
#
|
61
|
+
# def zscore(member)
|
62
|
+
# redis.zscore(@key, member).to_f
|
63
|
+
# end
|
64
|
+
#
|
65
|
+
# def zremrangebyscore(min, max)
|
66
|
+
# redis.zremrangebyscore(@key, min, max)
|
67
|
+
# end
|
68
|
+
#
|
69
|
+
# def zremrangebyrank(start, _end)
|
70
|
+
# redis.zremrangebyrank(@key, start, _end)
|
71
|
+
# end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|