rediline 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,3 @@
1
+ .DS_Store
2
+ .bundle/
3
+
data/.rvmrc ADDED
@@ -0,0 +1,2 @@
1
+ rvm use 1.9.2@rediline
2
+ rvm_gemset_create_on_use_flag=1
data/Gemfile ADDED
@@ -0,0 +1,19 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem "redis", '2.0.7'
4
+ gem "redis-namespace", '0.10.0'
5
+ gem "json"
6
+ gem "i18n"
7
+ gem "activesupport", "~> 3.0.0"
8
+ gem "activemodel", '~> 3.0.0'
9
+
10
+ group :development do
11
+ gem "jeweler"
12
+ end
13
+
14
+ group :test do
15
+ gem "rspec", '~> 2.0.0.beta'
16
+ gem "rspec-rails", '~> 2.0.0.beta'
17
+ gem "mocha", '0.9.8'
18
+ gem "timecop", '0.3.5'
19
+ end
@@ -0,0 +1,63 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ activemodel (3.0.0)
5
+ activesupport (= 3.0.0)
6
+ builder (~> 2.1.2)
7
+ i18n (~> 0.4.1)
8
+ activesupport (3.0.0)
9
+ builder (2.1.2)
10
+ diff-lcs (1.1.2)
11
+ gemcutter (0.6.1)
12
+ git (1.2.5)
13
+ i18n (0.4.1)
14
+ jeweler (1.4.0)
15
+ gemcutter (>= 0.1.0)
16
+ git (>= 1.2.5)
17
+ rubyforge (>= 2.0.0)
18
+ json (1.4.6)
19
+ json_pure (1.4.6)
20
+ mocha (0.9.8)
21
+ rake
22
+ nokogiri (1.4.3.1)
23
+ rack (1.2.1)
24
+ rack-test (0.5.4)
25
+ rack (>= 1.0)
26
+ rake (0.8.7)
27
+ redis (2.0.7)
28
+ redis-namespace (0.10.0)
29
+ redis (< 3.0.0)
30
+ rspec (2.0.0.beta.18)
31
+ rspec-core (= 2.0.0.beta.18)
32
+ rspec-expectations (= 2.0.0.beta.18)
33
+ rspec-mocks (= 2.0.0.beta.18)
34
+ rspec-core (2.0.0.beta.18)
35
+ rspec-expectations (2.0.0.beta.18)
36
+ diff-lcs (>= 1.1.2)
37
+ rspec-mocks (2.0.0.beta.18)
38
+ rspec-rails (2.0.0.beta.18)
39
+ rspec (>= 2.0.0.beta.14)
40
+ webrat (>= 0.7.0)
41
+ rubyforge (2.0.4)
42
+ json_pure (>= 1.1.7)
43
+ timecop (0.3.5)
44
+ webrat (0.7.1)
45
+ nokogiri (>= 1.2.0)
46
+ rack (>= 1.0)
47
+ rack-test (>= 0.5.3)
48
+
49
+ PLATFORMS
50
+ ruby
51
+
52
+ DEPENDENCIES
53
+ activemodel (~> 3.0.0)
54
+ activesupport (~> 3.0.0)
55
+ i18n
56
+ jeweler
57
+ json
58
+ mocha (= 0.9.8)
59
+ redis (= 2.0.7)
60
+ redis-namespace (= 0.10.0)
61
+ rspec (~> 2.0.0.beta)
62
+ rspec-rails (~> 2.0.0.beta)
63
+ timecop (= 0.3.5)
@@ -0,0 +1,143 @@
1
+ # RediLine
2
+ *Redis Backed Timeline*
3
+
4
+ Rediline is a ruby library which intends to allow you to create timelines for your users in ruby.
5
+
6
+ ## Installation
7
+
8
+ Add the following to your Gemfile :
9
+
10
+ gem "rediline"
11
+
12
+ Bundle install it and you're done, it's installed.
13
+
14
+ ## Configuring the user model
15
+
16
+ Your user model should look like the following :
17
+
18
+ class User < ActiveRecord::Base
19
+ include Redline::User
20
+
21
+ rediline :timeline do
22
+ list :egocentric do
23
+ [user]
24
+ end
25
+ end
26
+ end
27
+
28
+ This will create a timeline which will be called "timeline", with one list named "egocentric".
29
+ In this list, you must return an array of all the users which will see the event created there.
30
+
31
+ For exemple, if your users have friends, you could do the following :
32
+
33
+ class User < ActiveRecord::Base
34
+ include Redline::User
35
+
36
+ rediline :timeline do
37
+ list :egocentric do
38
+ [user]
39
+ end
40
+
41
+ list :public do
42
+ user.friends.all
43
+ end
44
+ end
45
+ end
46
+
47
+ With this, any of your users will have two lists : an "egocentric" one, which will contain all the actions made by this same user.
48
+ And a "public" one, which will contain all the actions made by this user's friends.
49
+
50
+ You can retrieve a list's actions with the each method.
51
+
52
+ User.first.timeline.each(:egocentric) do |action|
53
+ p action.inspect
54
+ end
55
+
56
+ ## Configuring the objects models and triggering events
57
+
58
+ You can potentially trigger events in any kind of model, whether they are linked to an ORM or not.
59
+ However, when including `Rediline::Object`, your model should be compatible with `ActiveModel::Callbacks`.
60
+
61
+ Here's what your model could look like :
62
+
63
+ class Post < ActiveRecord::Base
64
+ include Redline::Object
65
+
66
+ redline :timeline,
67
+ :user => :owner,
68
+ :verb => :created,
69
+ :when => :after_create
70
+ end
71
+
72
+ An after_create event will be added to this model and triggered every time a new post is created.
73
+ By default, we rely on the "user" method. But you can overwrite it to anything else like we do with "owner" here.
74
+
75
+ You can also use procs to define the values.
76
+
77
+ class Post < ActiveRecord::Base
78
+ include Redline::Object
79
+
80
+ redline :timeline,
81
+ :user => lambda {|post| post.owner },
82
+ :verb => :created,
83
+ :when => :after_create
84
+ end
85
+
86
+ ## Configuration
87
+
88
+ By default, we use localhost:6379 as the redis server with the namespace "rediline".
89
+ However, you can change that.
90
+
91
+ Rediline has a redis setter which can be given a string or a Redis object.
92
+ This means if you're already using Redis in your app, Rediline can re-use the existing connection.
93
+
94
+ String: `Rediline.redis = 'localhost:6379'`
95
+ Redis: `Resque.redis = $redis`
96
+
97
+ In a rails app, I have an initializer in `config/initializers/rediline.rb` where I load `config/rediline.yml` and set the redis information appropriately.
98
+
99
+ My rediline.yml file is the following :
100
+
101
+ development:
102
+ host: localhost
103
+ port: 6379
104
+ test:
105
+ host: localhost
106
+ port: 6379
107
+
108
+ And the initializer :
109
+
110
+ rediline_config = YAML::load(Rails.root.join('config', 'rediline.yml').read)[Rails.env]
111
+ Rediline.redis = Redis.new(
112
+ :host => rediline_config['host'],
113
+ :port => rediline_config['port'],
114
+ :password => rediline_config['password']
115
+ )
116
+
117
+ ## Development
118
+
119
+ Want to hack on Rediline?
120
+
121
+ First clone the repo and run the tests:
122
+
123
+ git clone git://github.com/dmathieu/rediline.git
124
+ cd rediline
125
+ bundle install
126
+ rake test
127
+
128
+ If the tests do not pass make sure you have Redis installed
129
+ correctly.
130
+
131
+
132
+ ## Contributing
133
+
134
+ Once you've made your great commits:
135
+
136
+ 1. [Fork][1] Rediline
137
+ 2. Create a topic branch - `git checkout -b my_branch`
138
+ 3. Push to your branch - `git push origin my_branch`
139
+ 4. Create an [Issue][2] with a link to your branch
140
+ 5. That's it!
141
+
142
+ [1]: http://help.github.com/forking/
143
+ [2]: http://github.com/dmathieu/rediline/issues
@@ -0,0 +1,34 @@
1
+ # encoding: UTF-8
2
+ require "rubygems"
3
+ require "bundler/setup"
4
+
5
+ #
6
+ # The rspec tasks
7
+ #
8
+ require 'rspec/core'
9
+ require 'rspec/core/rake_task'
10
+ task :default => :spec
11
+ RSpec::Core::RakeTask.new(:spec)
12
+
13
+ #
14
+ # Jeweler
15
+ #
16
+ begin
17
+ require 'jeweler'
18
+ Jeweler::Tasks.new do |gemspec|
19
+ gemspec.name = "rediline"
20
+ gemspec.summary = "Redis Backed Timeline"
21
+ gemspec.description = "Timeline library"
22
+ gemspec.email = "42@dmathieu.com"
23
+ gemspec.homepage = "http://github.com/dmathieu/rediline"
24
+ gemspec.authors = ["Damien MATHIEU"]
25
+
26
+ gemspec.add_dependency "redis", '2.0.7'
27
+ gemspec.add_dependency "redis-namespace", '0.10.0'
28
+ gemspec.add_dependency "json"
29
+ gemspec.add_dependency "i18n"
30
+ gemspec.add_dependency "activesupport", "3.0.0"
31
+ end
32
+ rescue LoadError
33
+ puts "Jeweler not available. Install it with: gem install jeweler"
34
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
data/init.rb ADDED
@@ -0,0 +1,4 @@
1
+ require "rubygems"
2
+ require "bundler/setup"
3
+
4
+ require 'rediline'
@@ -0,0 +1,16 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'active_model'
4
+
5
+ require 'redis/namespace'
6
+ require 'rediline/redis'
7
+ require 'rediline/entry'
8
+ require 'rediline/timeline'
9
+ require 'rediline/object'
10
+ require 'rediline/user'
11
+
12
+ module Rediline
13
+ extend Rediline::Redis
14
+
15
+
16
+ end
@@ -0,0 +1,65 @@
1
+ require 'json'
2
+ require 'active_support/core_ext'
3
+
4
+ module Rediline
5
+
6
+ class Entry
7
+ attr_reader :content
8
+
9
+ def initialize(content)
10
+ @content = parse(content)
11
+ @objects = {}
12
+ end
13
+
14
+ def to_json
15
+ @content.to_json
16
+ end
17
+
18
+ def method_missing(name, *args)
19
+ return content[name] if content.include?(name)
20
+ if content.include?(:"#{name}_object") && content.include?(:"#{name}_object")
21
+ return @objects[name] ||= content[:"#{name}_object"].constantize.find(content[:"#{name}_id"])
22
+ end
23
+ super
24
+ end
25
+
26
+ def respond_to?(name, *args)
27
+ return true if content.include?(name.to_s)
28
+ return true if content.include?("#{name}_object") && content.include?("#{name}_object")
29
+ super
30
+ end
31
+
32
+ private
33
+ def parse(string)
34
+ if string.is_a?(String)
35
+ string = JSON.parse(string).symbolize_keys
36
+
37
+ [:user_id, :user_object, :object_id, :object_object, :verb].each do |f|
38
+ raise "invalid content : missing field #{f}" if string[f].nil?
39
+ end
40
+ else
41
+ string.symbolize_keys!
42
+
43
+ [:user, :object, :verb].each do |f|
44
+ raise "invalid content : missing field #{f}" if string[f].nil?
45
+ end
46
+ string[:created_at] = string[:created_at].nil? ? Time.now.utc.to_s : string[:created_at].to_s
47
+
48
+ object = string[:object]
49
+ string.keys.each do |k|
50
+ string[k] = string[k].call(object) if string[k].is_a?(Proc)
51
+
52
+ case string[k]
53
+ when String, Symbol, Numeric
54
+ next
55
+ else
56
+ string[:"#{k}_object"] = string[k].class.to_s
57
+ string[:"#{k}_id"] = string[k].id.to_s
58
+ string.delete k
59
+ end
60
+ end
61
+ end
62
+ string
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,57 @@
1
+ module Rediline
2
+ module Object
3
+
4
+ def self.included(model)
5
+ model.send(:extend, ClassMethods)
6
+
7
+ model.class_eval do
8
+ private
9
+ def rediline_key(field_name, entry, type, user=nil)
10
+ raise "no entry provided" if entry.nil?
11
+ user = entry.user if user.nil?
12
+ "#{field_name.to_s}:#{user.class.to_s}.#{user.id.to_s}:#{type}"
13
+ end
14
+
15
+ def rediline_insert!(entry, key)
16
+ Rediline.redis.del(key) unless Rediline.redis.type(key) == 'list'
17
+ Rediline.redis.rpush(key, entry.to_json)
18
+ end
19
+ end
20
+ end
21
+
22
+ module ClassMethods
23
+ def rediline(field_name, attrs)
24
+ attrs.symbolize_keys!
25
+ callback = attrs.delete :when
26
+
27
+ define_method "rediline_#{callback}" do
28
+ if attrs.frozen?
29
+ entry = Rediline::Entry.new(attrs.dup)
30
+ else
31
+ attrs[:object] = self
32
+ case attrs[:user]
33
+ when Symbol, String
34
+ attrs[:user] = send(attrs[:user])
35
+ when Proc
36
+ attrs[:user] = attrs[:user].call(self)
37
+ when nil
38
+ attrs[:user] = send(:user)
39
+ end
40
+ attrs.freeze
41
+ entry = Rediline::Entry.new(attrs.dup)
42
+ end
43
+
44
+ entry.user.send(field_name).lists.each_pair do |k, v|
45
+ v.each do |user|
46
+ rediline_insert! entry, rediline_key(field_name, entry, k, user)
47
+ end
48
+ end
49
+
50
+ true
51
+ end
52
+ send(callback, "rediline_#{callback}")
53
+ end
54
+
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,35 @@
1
+ module Rediline
2
+
3
+ module Redis
4
+
5
+ # Accepts:
6
+ # 1. A 'hostname:port' string
7
+ # 2. A 'hostname:port:db' string (to select the Redis db)
8
+ # 3. An instance of `Redis`, `Redis::Client`, or `Redis::Namespace`.
9
+ def redis=(server)
10
+ case server
11
+ when String
12
+ host, port, db = server.split(':')
13
+ redis = ::Redis.new(:host => host, :port => port,
14
+ :thread_safe => true, :db => db)
15
+ @redis = ::Redis::Namespace.new(:rediline, :redis => redis)
16
+ when ::Redis, ::Redis::Client
17
+ @redis = ::Redis::Namespace.new(:rediline, :redis => server)
18
+ when ::Redis::Namespace
19
+ @redis = server
20
+ when nil
21
+ @redis = nil
22
+ else
23
+ raise "I don't know what to do with #{server.inspect}"
24
+ end
25
+ end
26
+
27
+ # Returns the current Redis connection. If none has been created, will
28
+ # create a new one.
29
+ def redis
30
+ return @redis if @redis
31
+ self.redis = 'localhost:6379'
32
+ self.redis
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,7 @@
1
+ require 'rediline/timeline/user'
2
+
3
+ module Rediline
4
+ module Timeline
5
+
6
+ end
7
+ end
@@ -0,0 +1,42 @@
1
+ module Rediline
2
+ module Timeline
3
+ class User
4
+ attr_reader :field_name, :user, :block, :lists
5
+
6
+ def initialize(field_name, user, block)
7
+ @field_name, @user, @block = field_name, user
8
+ @lists = {}
9
+ instance_eval(&block)
10
+ end
11
+
12
+ def each(type)
13
+ raise "you must provide a block" unless block_given?
14
+ (0..count(type)-1).each do |i|
15
+ data = Rediline.redis.lindex(key(type), i)
16
+ yield Rediline::Entry.new(data)
17
+ end
18
+ end
19
+
20
+ def to_a(type)
21
+ result = Array.new
22
+ self.each(type) do |entry|
23
+ result << entry
24
+ end
25
+ result
26
+ end
27
+
28
+ def count(type)
29
+ Rediline.redis.llen(key(type))
30
+ end
31
+
32
+ def list(name, &block)
33
+ @lists[name] = instance_eval(&block)
34
+ end
35
+
36
+ private
37
+ def key(type)
38
+ "#{field_name.to_s}:#{@user.class.to_s}.#{@user.id.to_s}:#{type}"
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,15 @@
1
+ module Rediline
2
+ module User
3
+
4
+ def self.included(model)
5
+ model.extend(ClassMethods)
6
+ end
7
+ module ClassMethods
8
+ def rediline(field_name, &block)
9
+ define_method field_name.to_sym do
10
+ Rediline::Timeline::User.new(field_name.to_sym, self, block)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,89 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{rediline}
8
+ s.version = "0.0.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Damien MATHIEU"]
12
+ s.date = %q{2010-09-23}
13
+ s.description = %q{Timeline library}
14
+ s.email = %q{42@dmathieu.com}
15
+ s.extra_rdoc_files = [
16
+ "README.md"
17
+ ]
18
+ s.files = [
19
+ ".gitignore",
20
+ ".rvmrc",
21
+ "Gemfile",
22
+ "Gemfile.lock",
23
+ "README.md",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "init.rb",
27
+ "lib/rediline.rb",
28
+ "lib/rediline/entry.rb",
29
+ "lib/rediline/object.rb",
30
+ "lib/rediline/redis.rb",
31
+ "lib/rediline/timeline.rb",
32
+ "lib/rediline/timeline/user.rb",
33
+ "lib/rediline/user.rb",
34
+ "rediline.gemspec",
35
+ "spec/entry_spec.rb",
36
+ "spec/object_spec.rb",
37
+ "spec/redis_spec.rb",
38
+ "spec/spec_helper.rb",
39
+ "spec/support/timeline.rb",
40
+ "spec/support/timeline_owner.rb",
41
+ "spec/support/user.rb",
42
+ "spec/timeline/user_spec.rb",
43
+ "spec/timeline_spec.rb",
44
+ "spec/user_spec.rb"
45
+ ]
46
+ s.homepage = %q{http://github.com/dmathieu/rediline}
47
+ s.rdoc_options = ["--charset=UTF-8"]
48
+ s.require_paths = ["lib"]
49
+ s.rubygems_version = %q{1.3.7}
50
+ s.summary = %q{Redis Backed Timeline}
51
+ s.test_files = [
52
+ "spec/entry_spec.rb",
53
+ "spec/object_spec.rb",
54
+ "spec/redis_spec.rb",
55
+ "spec/spec_helper.rb",
56
+ "spec/support/timeline.rb",
57
+ "spec/support/timeline_owner.rb",
58
+ "spec/support/user.rb",
59
+ "spec/timeline/user_spec.rb",
60
+ "spec/timeline_spec.rb",
61
+ "spec/user_spec.rb"
62
+ ]
63
+
64
+ if s.respond_to? :specification_version then
65
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
66
+ s.specification_version = 3
67
+
68
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
69
+ s.add_runtime_dependency(%q<redis>, ["= 2.0.7"])
70
+ s.add_runtime_dependency(%q<redis-namespace>, ["= 0.10.0"])
71
+ s.add_runtime_dependency(%q<json>, [">= 0"])
72
+ s.add_runtime_dependency(%q<i18n>, [">= 0"])
73
+ s.add_runtime_dependency(%q<activesupport>, ["= 3.0.0"])
74
+ else
75
+ s.add_dependency(%q<redis>, ["= 2.0.7"])
76
+ s.add_dependency(%q<redis-namespace>, ["= 0.10.0"])
77
+ s.add_dependency(%q<json>, [">= 0"])
78
+ s.add_dependency(%q<i18n>, [">= 0"])
79
+ s.add_dependency(%q<activesupport>, ["= 3.0.0"])
80
+ end
81
+ else
82
+ s.add_dependency(%q<redis>, ["= 2.0.7"])
83
+ s.add_dependency(%q<redis-namespace>, ["= 0.10.0"])
84
+ s.add_dependency(%q<json>, [">= 0"])
85
+ s.add_dependency(%q<i18n>, [">= 0"])
86
+ s.add_dependency(%q<activesupport>, ["= 3.0.0"])
87
+ end
88
+ end
89
+
@@ -0,0 +1,127 @@
1
+ # encoding: UTF-8
2
+ require 'spec_helper'
3
+
4
+ describe Rediline::Entry do
5
+
6
+ it 'should initialize' do
7
+ lambda do
8
+ Rediline::Entry.new valid_hash
9
+ end.should_not raise_error
10
+ end
11
+
12
+ it 'should initialize with a hash' do
13
+ lambda do
14
+ Rediline::Entry.new valid_hash
15
+ end.should_not raise_error
16
+ end
17
+
18
+ describe 'when initializing with a string' do
19
+ [:object_object, :object_id, :user_object, :user_id, :verb].each do |f|
20
+ it "should require a #{f}" do
21
+ c = valid_json
22
+ c.delete f
23
+ lambda do
24
+ Rediline::Entry.new c.to_json
25
+ end.should raise_error "invalid content : missing field #{f}"
26
+ end
27
+
28
+ it "should define the #{f} attribute" do
29
+ entry = Rediline::Entry.new valid_json.to_json
30
+ entry.send(f).should_not be_nil
31
+ end
32
+ end
33
+
34
+ [:object, :user].each do |o|
35
+ it "should not have any #{o} object in its content" do
36
+ entry = Rediline::Entry.new valid_json.to_json
37
+ entry.content[o].should be_nil
38
+ end
39
+ end
40
+ end
41
+
42
+ describe 'when initializing with a hash' do
43
+ [:object, :user, :verb].each do |f|
44
+ it "should require a #{f}" do
45
+ c = valid_hash
46
+ c.delete f
47
+ lambda do
48
+ Rediline::Entry.new c
49
+ end.should raise_error "invalid content : missing field #{f}"
50
+ end
51
+
52
+ it "should define the #{f} attribute" do
53
+ entry = Rediline::Entry.new valid_hash
54
+ entry.send(f).should_not be_nil
55
+ end
56
+ end
57
+
58
+ [:object, :user].each do |o|
59
+ it "should not have any #{o} object in its content" do
60
+ entry = Rediline::Entry.new valid_json.to_json
61
+ entry.content[o].should be_nil
62
+ end
63
+ end
64
+
65
+ it 'should allow us to define an other object' do
66
+ c = valid_hash
67
+ c[:second_object] = TestingTimelineObject.new(666)
68
+ entry = Rediline::Entry.new c
69
+ entry.second_object.should be_kind_of(TestingTimelineObject)
70
+ entry.second_object.id.should eql('666')
71
+ end
72
+
73
+ it 'should allow us to define an other object as a proc' do
74
+ c = valid_hash
75
+ c[:second_object] = lambda {|o| o.id }
76
+ entry = Rediline::Entry.new c
77
+ entry.second_object.should eql(42)
78
+ end
79
+ end
80
+
81
+ describe 'creation date' do
82
+ it 'should define the creation date' do
83
+ Timecop.freeze(Time.now) do
84
+ entry = Rediline::Entry.new valid_hash
85
+ entry.created_at.should eql(Time.now.utc.to_s)
86
+ end
87
+ end
88
+
89
+ it 'should be possible to define our own creation date' do
90
+ Timecop.freeze(Time.now) do
91
+ c = valid_hash
92
+ c[:created_at] = Date.new(2010, 01, 01).to_s
93
+ entry = Rediline::Entry.new c
94
+ entry.created_at.should eql(Date.new(2010, 01, 01).to_s)
95
+ end
96
+ end
97
+ end
98
+
99
+ describe 'to_json' do
100
+ it 'should return the attributes in json' do
101
+ entry = Rediline::Entry.new valid_hash
102
+ entry.to_json.should be_kind_of(String)
103
+ json = JSON.parse(entry.to_json)
104
+ valid_json.each_pair do |k, v|
105
+ valid_json[k].to_s.should eql(json[k.to_s])
106
+ end
107
+ end
108
+ end
109
+
110
+ def valid_hash
111
+ {
112
+ :object => TestingTimelineObject.new(42),
113
+ :user => User.new(1),
114
+ :verb => :created
115
+ }
116
+ end
117
+
118
+ def valid_json
119
+ {
120
+ :object_object => "TestingTimelineObject",
121
+ :object_id => 42,
122
+ :user_object => "User",
123
+ :user_id => 1,
124
+ :verb => :created
125
+ }
126
+ end
127
+ end
@@ -0,0 +1,99 @@
1
+ # encoding: UTF-8
2
+ require 'spec_helper'
3
+
4
+ describe Rediline::Object do
5
+ before :each do
6
+ @object = TestingTimelineObject.new(1, User.new(1))
7
+ @entry = Rediline::Entry.new(valid_log)
8
+ end
9
+
10
+ it 'should have the rediline method' do
11
+ TestingTimelineObject.respond_to?(:rediline).should be_true
12
+ end
13
+
14
+ [:after_create, :before_destroy].each do |c|
15
+ it "should create the #{c} callback" do
16
+ @object.respond_to?("rediline_#{c}".to_sym).should be_true
17
+ end
18
+
19
+ it 'should not fail to call the callback' do
20
+ lambda do
21
+ @object.send("rediline_#{c}".to_sym)
22
+ end.should_not raise_error
23
+ end
24
+
25
+ it 'should add the log for the egocentric list' do
26
+ length = User.new(1).timeline.count(:egocentric)
27
+ @object.send("rediline_#{c}".to_sym)
28
+ User.new(1).timeline.count(:egocentric).should eql(length + 1)
29
+ end
30
+
31
+ it 'should add the logs for the public list' do
32
+ length1 = User.new(15).timeline.count(:public)
33
+ length2 = User.new(16).timeline.count(:public)
34
+ @object.send("rediline_#{c}".to_sym)
35
+ User.new(15).timeline.count(:public).should eql(length1 + 1)
36
+ User.new(16).timeline.count(:public).should eql(length2 + 1)
37
+ end
38
+
39
+ describe 'not default user field' do
40
+ before :each do
41
+ @object = TestingTimelineObjectWithOwner.new(1, User.new(1))
42
+ end
43
+
44
+ it 'should not fail to call the callback' do
45
+ lambda do
46
+ @object.send("rediline_#{c}".to_sym)
47
+ end
48
+ end
49
+
50
+ it 'should add the log for the egocentric list' do
51
+ length = User.new(1).timeline.count(:egocentric)
52
+ @object.send("rediline_#{c}".to_sym)
53
+ User.new(1).timeline.count(:egocentric).should eql(length + 1)
54
+ end
55
+
56
+ it 'should add the logs for the public list' do
57
+ length1 = User.new(15).timeline.count(:public)
58
+ length2 = User.new(16).timeline.count(:public)
59
+ @object.send("rediline_#{c}".to_sym)
60
+ User.new(15).timeline.count(:public).should eql(length1 + 1)
61
+ User.new(16).timeline.count(:public).should eql(length2 + 1)
62
+ end
63
+ end
64
+ end
65
+
66
+ describe 'rediline_key' do
67
+ it 'should return the timeline\'s key' do
68
+ @object.send(:rediline_key, :timeline, @entry, :egocentric).should eql('timeline:User.1:egocentric')
69
+ end
70
+
71
+ it 'should define a different user' do
72
+ @object.send(:rediline_key, :timeline, @entry, :egocentric, User.new(15)).should eql('timeline:User.15:egocentric')
73
+ end
74
+ end
75
+
76
+ describe 'rediline_insert!' do
77
+ it 'should delete the key if it was not a list' do
78
+ Rediline.redis.set "testing", "string key"
79
+ lambda do
80
+ @object.send(:rediline_insert!, {:test => true}, "testing")
81
+ end.should_not raise_error
82
+ Rediline.redis.type("testing").should eql('list')
83
+ end
84
+
85
+ it 'should push a new key to the list' do
86
+ length = Rediline.redis.llen("testing")
87
+ @object.send(:rediline_insert!, {:test => true}, "testing")
88
+ Rediline.redis.llen("testing").should eql(length + 1)
89
+ end
90
+ end
91
+
92
+ def valid_log
93
+ {
94
+ :object => @object,
95
+ :user => User.new(1),
96
+ :verb => :created
97
+ }
98
+ end
99
+ end
@@ -0,0 +1,63 @@
1
+ # encoding: UTF-8
2
+ require 'spec_helper'
3
+
4
+ describe Rediline do
5
+
6
+ describe 'redis=' do
7
+ it 'should define a redis server with a string' do
8
+ lambda do
9
+ Rediline.redis = 'localhost:6379'
10
+ end.should_not raise_error
11
+ end
12
+ it 'should define a redis server with a string/db' do
13
+ lambda do
14
+ Rediline.redis = 'localhost:6379:dmathieu'
15
+ end.should_not raise_error
16
+ end
17
+
18
+ it 'should define a redis server with a Redis object' do
19
+ lambda do
20
+ Rediline.redis = ::Redis.new(:host => 'localhost', :port => '6379',
21
+ :thread_safe => true, :db => 'dmathieu')
22
+ end.should_not raise_error
23
+ end
24
+
25
+ it 'should define a redis server with a Redis::Client object' do
26
+ lambda do
27
+ Rediline.redis = ::Redis::Client.new(:host => 'localhost', :port => '6379',
28
+ :thread_safe => true, :db => 'dmathieu')
29
+ end.should_not raise_error
30
+ end
31
+
32
+ it 'should define a redis server with a Redis::Namespace object' do
33
+ lambda do
34
+ redis = ::Redis.new(:host => 'localhost', :port => '6379',
35
+ :thread_safe => true, :db => 'dmathieu')
36
+ Rediline.redis = ::Redis::Namespace.new(:dmathieu, :redis => redis)
37
+ end.should_not raise_error
38
+ end
39
+
40
+ it 'should close the connection' do
41
+ lambda do
42
+ Rediline.redis = nil
43
+ end.should_not raise_error
44
+ end
45
+
46
+ it 'should not know what to do with that' do
47
+ lambda do
48
+ Rediline.redis = Rediline::Redis
49
+ end.should raise_error
50
+ end
51
+ end
52
+
53
+ describe 'redis' do
54
+ it 'should return the redis object' do
55
+ Rediline.redis.should be_kind_of Redis::Namespace
56
+ end
57
+
58
+ it 'should initialize a new redis object' do
59
+ Rediline.redis = nil
60
+ Rediline.redis.should be_kind_of Redis::Namespace
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,7 @@
1
+ # encoding: UTF-8
2
+ require 'rediline'
3
+
4
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
5
+ require 'rspec'
6
+ require 'timecop'
7
+ require 'mocha'
@@ -0,0 +1,34 @@
1
+ class TestingTimelineObject
2
+ extend ActiveModel::Callbacks
3
+ define_model_callbacks :create, :destroy
4
+ include Rediline::Object
5
+
6
+ rediline :timeline,
7
+ :user => :user,
8
+ :verb => :created,
9
+ :when => :after_create
10
+ rediline :timeline,
11
+ :user => lambda {|o| o.user },
12
+ :verb => :destroyed,
13
+ :when => :before_destroy
14
+
15
+ attr_reader :id, :user
16
+ def initialize(id, user=nil)
17
+ @id, @user = id, user
18
+ end
19
+
20
+ def self.find(id, user=nil)
21
+ self.new(id, user)
22
+ end
23
+
24
+ def create
25
+ _run_create_callbacks do
26
+ # Your create action methods here
27
+ end
28
+ end
29
+ def destroy
30
+ _run_destroy_callbacks do
31
+ # Your destroy action methods here
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,34 @@
1
+ class TestingTimelineObjectWithOwner
2
+ extend ActiveModel::Callbacks
3
+ define_model_callbacks :create, :destroy
4
+ include Rediline::Object
5
+
6
+ rediline :timeline,
7
+ :user => :owner,
8
+ :verb => :created,
9
+ :when => :after_create
10
+ rediline :timeline,
11
+ :user => :owner,
12
+ :verb => :created,
13
+ :when => :before_destroy
14
+
15
+ attr_reader :id, :owner
16
+ def initialize(id, owner=nil)
17
+ @id, @owner = id, owner
18
+ end
19
+
20
+ def self.find(id, owner=nil)
21
+ self.new(id, owner)
22
+ end
23
+
24
+ def create
25
+ _run_create_callbacks do
26
+ # Your create action methods here
27
+ end
28
+ end
29
+ def destroy
30
+ _run_destroy_callbacks do
31
+ # Your destroy action methods here
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,20 @@
1
+ class User
2
+ include Rediline::User
3
+ rediline :timeline do
4
+ list :egocentric do
5
+ [user]
6
+ end
7
+ list :public do
8
+ [User.new(15), User.new(16)]
9
+ end
10
+ end
11
+
12
+ attr_reader :id
13
+ def initialize(id)
14
+ @id = id
15
+ end
16
+
17
+ def self.find(id)
18
+ self.new(id)
19
+ end
20
+ end
@@ -0,0 +1,71 @@
1
+ # encoding: UTF-8
2
+ require 'spec_helper'
3
+
4
+ describe Rediline::Timeline::User do
5
+ before :each do
6
+ @timeline = User.new(1).timeline
7
+ end
8
+
9
+ describe 'lists' do
10
+ it 'should have defined the egocentric list' do
11
+ @timeline.lists.should_not be_empty
12
+ end
13
+ it 'should have the egocentric list with one user' do
14
+ @timeline.lists[:egocentric].length.should eql(1)
15
+ end
16
+ it 'should have the public list with two users' do
17
+ @timeline.lists[:public].length.should eql(2)
18
+ end
19
+
20
+ it 'should be able to add a new list' do
21
+ @timeline.lists[:testing].should be_nil
22
+ @timeline.list(:testing) { [user] }
23
+ @timeline.lists[:testing].should_not be_nil
24
+ end
25
+ end
26
+
27
+ describe 'each' do
28
+ it 'should require a block' do
29
+ lambda do
30
+ @timeline.each(:egocentric)
31
+ end.should raise_error 'you must provide a block'
32
+ end
33
+
34
+ it 'should loop through the list' do
35
+ @timeline.each(:egocentric) do |entry|
36
+ entry.should be_kind_of(Rediline::Entry)
37
+ end
38
+ end
39
+
40
+ [:object, :user].each do |o|
41
+ it "should not have any #{o} in it\'s values" do
42
+ @timeline.each(:egocentric) do |entry|
43
+ entry.content[o].should be_nil
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ describe 'to_a' do
50
+ it 'should return an array' do
51
+ @timeline.to_a(:egocentric).should be_kind_of(Array)
52
+ end
53
+ it 'should have the entries' do
54
+ @timeline.to_a(:egocentric).each do |entry|
55
+ entry.should be_kind_of(Rediline::Entry)
56
+ end
57
+ end
58
+ end
59
+
60
+ describe 'count' do
61
+ it 'should return an integer' do
62
+ @timeline.count(:egocentric).should be_kind_of(Integer)
63
+ end
64
+ end
65
+
66
+ describe 'key' do
67
+ it 'should return the timeline\'s key' do
68
+ @timeline.send(:key, :egocentric).should eql('timeline:User.1:egocentric')
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,6 @@
1
+ # encoding: UTF-8
2
+ require 'spec_helper'
3
+
4
+ describe Rediline::Timeline do
5
+
6
+ end
@@ -0,0 +1,13 @@
1
+ # encoding: UTF-8
2
+ require 'spec_helper'
3
+
4
+ describe Rediline::User do
5
+
6
+ it 'should have the timeline method' do
7
+ User.new(1).respond_to?(:timeline).should eql(true)
8
+ end
9
+
10
+ it 'should return a timeline object' do
11
+ User.new(1).timeline.should be_kind_of(Rediline::Timeline::User)
12
+ end
13
+ end
metadata ADDED
@@ -0,0 +1,169 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rediline
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - Damien MATHIEU
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-09-23 00:00:00 +02:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: redis
22
+ requirement: &id001 !ruby/object:Gem::Requirement
23
+ none: false
24
+ requirements:
25
+ - - "="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 2
29
+ - 0
30
+ - 7
31
+ version: 2.0.7
32
+ type: :runtime
33
+ prerelease: false
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: redis-namespace
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - "="
41
+ - !ruby/object:Gem::Version
42
+ segments:
43
+ - 0
44
+ - 10
45
+ - 0
46
+ version: 0.10.0
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: *id002
50
+ - !ruby/object:Gem::Dependency
51
+ name: json
52
+ requirement: &id003 !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ segments:
58
+ - 0
59
+ version: "0"
60
+ type: :runtime
61
+ prerelease: false
62
+ version_requirements: *id003
63
+ - !ruby/object:Gem::Dependency
64
+ name: i18n
65
+ requirement: &id004 !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ segments:
71
+ - 0
72
+ version: "0"
73
+ type: :runtime
74
+ prerelease: false
75
+ version_requirements: *id004
76
+ - !ruby/object:Gem::Dependency
77
+ name: activesupport
78
+ requirement: &id005 !ruby/object:Gem::Requirement
79
+ none: false
80
+ requirements:
81
+ - - "="
82
+ - !ruby/object:Gem::Version
83
+ segments:
84
+ - 3
85
+ - 0
86
+ - 0
87
+ version: 3.0.0
88
+ type: :runtime
89
+ prerelease: false
90
+ version_requirements: *id005
91
+ description: Timeline library
92
+ email: 42@dmathieu.com
93
+ executables: []
94
+
95
+ extensions: []
96
+
97
+ extra_rdoc_files:
98
+ - README.md
99
+ files:
100
+ - .gitignore
101
+ - .rvmrc
102
+ - Gemfile
103
+ - Gemfile.lock
104
+ - README.md
105
+ - Rakefile
106
+ - VERSION
107
+ - init.rb
108
+ - lib/rediline.rb
109
+ - lib/rediline/entry.rb
110
+ - lib/rediline/object.rb
111
+ - lib/rediline/redis.rb
112
+ - lib/rediline/timeline.rb
113
+ - lib/rediline/timeline/user.rb
114
+ - lib/rediline/user.rb
115
+ - rediline.gemspec
116
+ - spec/entry_spec.rb
117
+ - spec/object_spec.rb
118
+ - spec/redis_spec.rb
119
+ - spec/spec_helper.rb
120
+ - spec/support/timeline.rb
121
+ - spec/support/timeline_owner.rb
122
+ - spec/support/user.rb
123
+ - spec/timeline/user_spec.rb
124
+ - spec/timeline_spec.rb
125
+ - spec/user_spec.rb
126
+ has_rdoc: true
127
+ homepage: http://github.com/dmathieu/rediline
128
+ licenses: []
129
+
130
+ post_install_message:
131
+ rdoc_options:
132
+ - --charset=UTF-8
133
+ require_paths:
134
+ - lib
135
+ required_ruby_version: !ruby/object:Gem::Requirement
136
+ none: false
137
+ requirements:
138
+ - - ">="
139
+ - !ruby/object:Gem::Version
140
+ hash: 1586793397385962776
141
+ segments:
142
+ - 0
143
+ version: "0"
144
+ required_rubygems_version: !ruby/object:Gem::Requirement
145
+ none: false
146
+ requirements:
147
+ - - ">="
148
+ - !ruby/object:Gem::Version
149
+ segments:
150
+ - 0
151
+ version: "0"
152
+ requirements: []
153
+
154
+ rubyforge_project:
155
+ rubygems_version: 1.3.7
156
+ signing_key:
157
+ specification_version: 3
158
+ summary: Redis Backed Timeline
159
+ test_files:
160
+ - spec/entry_spec.rb
161
+ - spec/object_spec.rb
162
+ - spec/redis_spec.rb
163
+ - spec/spec_helper.rb
164
+ - spec/support/timeline.rb
165
+ - spec/support/timeline_owner.rb
166
+ - spec/support/user.rb
167
+ - spec/timeline/user_spec.rb
168
+ - spec/timeline_spec.rb
169
+ - spec/user_spec.rb