rediline 0.0.1

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.
@@ -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