activerecord-publishable 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,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7996fc0dec2bf180ae15316bbd309cc3d769d26e
4
+ data.tar.gz: ba8319dbb4a8b5d18f1258db438eae2ef9fd3264
5
+ SHA512:
6
+ metadata.gz: 7b9d8a175f343875fe6ee22096cdb2aa764ca3d94ae818958b48b4876c2458013876d6b6e0f8f1b16094b5d8ef1532d69fb1043965a45027be5c2ef621d9ec2d
7
+ data.tar.gz: cbf93e800ba381ab97238478e58c983bb60bb53dc521f0342fdb1d396307811f1f2383aed73d89878540778715a2527201b934b9f9656ec6c18edcaff6afc09f
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+ *.sqlite3
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem 'activerecord', '~> 4.2.0', require: 'active_record'
6
+ gem 'sqlite3'
7
+ gem 'active_model_serializers'
8
+
9
+ group :examples do
10
+ gem 'angelo'
11
+ gem 'celluloid-redis'
12
+ end
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Ray Zane
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
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
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,87 @@
1
+ # ActiveRecord::Publishable
2
+
3
+ Publish changes to your ActiveRecord models to Redis for use with PubSub. This allows you to easily implement Server-Sent events.
4
+
5
+ ![Preview](examples/sse/public/streaming.gif?raw=true)
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'activerecord-publishable'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install activerecord-publishable
22
+
23
+ ## Usage
24
+
25
+ To start publishing changes to your models, include the module and call `publishable`.
26
+
27
+ ```ruby
28
+ class Post < ActiveRecord::Base
29
+ include ActiveRecord::Publishable
30
+
31
+ publishable
32
+ end
33
+ ```
34
+
35
+ The `publishable` method creates an `after_commit` hook on create, update, and destroy. You can pass the `:on` option to only push certain events. It accepts all of the options you can pass to an `after_commit` hook, like `:if` and `:unless`.
36
+
37
+ ```ruby
38
+ publishable on: [:update, :destroy]
39
+ publishable on: :create, if: :some_condition?
40
+ publishable on: :create, unless: ->{ dont_push? }
41
+ ```
42
+
43
+ When pushing to Redis, your model will be serialized as JSON. If you have `ActiveModel::Serializers` loaded, your serializer will be looked up. Otherwise, `as_json` will be used.
44
+
45
+ You can pass options for serialization by using the `:serialize` option. The `:with` option allows you to override the serializer that is used.
46
+
47
+ ```ruby
48
+ publishable serialize: { with: OtherPostSerializer }
49
+ publishable serialize: { only: [:title, :content] }
50
+ ```
51
+
52
+ By default, events will be published to `<collection>:create`, `<collection>:update`, and `<collection>:destroy`, where collection is the plural name of your model. To override this behavior, you can pass the `:channel` option:
53
+
54
+ ```ruby
55
+ publishable on: :create, channel: "custom:create"
56
+ ```
57
+
58
+ Alternatively, you can override `channel_for_publishing`:
59
+
60
+ ```ruby
61
+ class Post < ActiveRecord::Base
62
+ include ActiveRecord::Publishable
63
+
64
+ publishable
65
+
66
+ def channel_for_publishing(action)
67
+ "posts:#{user.id}:#{action}"
68
+ end
69
+ end
70
+ ```
71
+
72
+ ## Running the demo app
73
+
74
+ The demo app provides an example of streaming changes to your models using Server-Sent events.
75
+
76
+ 1. Clone the repository
77
+ 2. Run `bundle install`
78
+ 3. Run `rake example`
79
+ 4. Open your browser to localhost:4567
80
+
81
+ ## Contributing
82
+
83
+ 1. Fork it ( https://github.com/rzane/activerecord-publishable/fork )
84
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
85
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
86
+ 4. Push to the branch (`git push origin my-new-feature`)
87
+ 5. Create a new Pull Request
@@ -0,0 +1,43 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'PublicActivityAggregate'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.rdoc')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ require "bundler/gem_tasks"
18
+
19
+ Bundler::GemHelper.install_tasks
20
+
21
+ require "rake/testtask"
22
+
23
+ Rake::TestTask.new :spec do |t|
24
+ t.libs << 'lib' << 'spec'
25
+ t.test_files = FileList['spec/**/*_spec.rb']
26
+ t.verbose = false
27
+ end
28
+
29
+ task :default => :spec
30
+
31
+ namespace :examples do
32
+ namespace :sse do
33
+ task :app do
34
+ ruby 'examples/sse/app.rb'
35
+ end
36
+
37
+ task :data do
38
+ ruby 'examples/sse/change_data.rb'
39
+ end
40
+ end
41
+
42
+ multitask :sse => ['sse:app', 'sse:data']
43
+ end
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'active_record/publishable/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "activerecord-publishable"
8
+ spec.version = ActiveRecord::Publishable::VERSION
9
+ spec.authors = ["Ray Zane"]
10
+ spec.email = ["raymondzane@gmail.com"]
11
+ spec.summary = %q{Redis PubSub for ActiveRecord models.}
12
+ spec.description = %q{Publish events to Redis when your models change.}
13
+ spec.homepage = "https://github.com/rzane/activerecord-publishable"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "redis"
22
+ spec.add_dependency "activerecord"
23
+ spec.add_dependency "activesupport"
24
+
25
+ spec.add_development_dependency "bundler", "~> 1.7"
26
+ spec.add_development_dependency "rake", "~> 10.0"
27
+ spec.add_development_dependency "minitest"
28
+ spec.add_development_dependency "mocha"
29
+ end
@@ -0,0 +1,28 @@
1
+ require 'bundler'
2
+ Bundler.require(:default, :examples)
3
+
4
+ ActiveRecord::Publishable.redis = Redis.new(driver: :celluloid)
5
+
6
+ class Angelo::Server
7
+ def report_errors?
8
+ true
9
+ end
10
+ end
11
+
12
+ class App < Angelo::Base
13
+ report_errors!
14
+
15
+ get '/' do
16
+ erb :index
17
+ end
18
+
19
+ eventsource '/events/posts' do |sse|
20
+ ActiveRecord::Publishable.redis.psubscribe('posts:*') do |on|
21
+ on.pmessage do |_, channel, message|
22
+ sse.event channel.split(':').last, message
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ App.run!
@@ -0,0 +1,44 @@
1
+ require 'bundler'
2
+ Bundler.require :default
3
+
4
+ require_relative './post'
5
+
6
+ # Running this file will create/update/delete records for demonstration purposess.
7
+ module Changer
8
+ extend self
9
+
10
+ def change action = nil
11
+ @index = index + 1
12
+ send action || [:create, :update, :destroy].sample
13
+ end
14
+
15
+ def create
16
+ post = Post.create content: "Post ##{index}"
17
+ puts "[Model] Created ##{post.id}"
18
+ end
19
+
20
+ def update
21
+ with_random_post 'Updated' do |post|
22
+ post.increment! :update_count
23
+ end
24
+ end
25
+
26
+ def destroy
27
+ with_random_post('Deleted') { |post| post.destroy }
28
+ end
29
+
30
+ private
31
+
32
+ def index; @index || 0 end
33
+
34
+ def with_random_post action
35
+ if post = Post.offset(rand(Post.count)).first
36
+ yield post
37
+ puts "[Model] #{action} ##{post.id}"
38
+ end
39
+ end
40
+ end
41
+
42
+ Post.delete_all
43
+ 5.times { Changer.change :create }
44
+ loop { Changer.change; sleep 1 }
@@ -0,0 +1,22 @@
1
+ require 'bundler'
2
+ Bundler.require :default
3
+
4
+ # Connect to an inmemory database
5
+ ActiveRecord::Base.establish_connection adapter: 'sqlite3',
6
+ database: 'db.sqlite3'
7
+
8
+ ActiveRecord::Base.raise_in_transactional_callbacks = true
9
+
10
+ conn = ActiveRecord::Base.connection
11
+ conn.create_table :posts do |t|
12
+ t.string :content
13
+ t.integer :update_count, default: 0
14
+ t.timestamps null: false
15
+ end unless conn.table_exists?(:posts)
16
+
17
+ # Here's the important part. Define our model and enable streaming.
18
+ class Post < ActiveRecord::Base
19
+ include ActiveRecord::Publishable
20
+
21
+ publishable
22
+ end
@@ -0,0 +1,70 @@
1
+ 'use strict';
2
+
3
+ window.App = function(url, verbs) {
4
+ this.verbs = verbs;
5
+ this.stream = new EventSource(url);
6
+ this.el = $('#posts');
7
+ };
8
+
9
+ $.extend(App.prototype, {
10
+ start: function() {
11
+ var post,
12
+ that = this;
13
+
14
+ this.stream.addEventListener('open', function() {
15
+ console.log('Listening to ' + this.url);
16
+ });
17
+
18
+ // Bind create, update, and destroy
19
+ $.each(this.verbs, function(index, verb) {
20
+ that.stream.addEventListener(verb, function(event) {
21
+ post = JSON.parse(event.data);
22
+ console.log(verb, post);
23
+ that[verb](post);
24
+ });
25
+ });
26
+ },
27
+
28
+ attributes: ['id', 'content', 'update_count', 'lastUpdated'],
29
+
30
+ render: function(post) {
31
+ post.lastUpdated = new Date(post.updated_at).toLocaleString();
32
+
33
+ var cols = $.map(this.attributes, function(attr) {
34
+ return $('<td>').text(post[attr]);
35
+ });
36
+
37
+ return $('<tr>').attr('id', 'post' + post.id).append(cols);
38
+ },
39
+
40
+ find: function(post) {
41
+ return this.el.find('#post' + post.id);
42
+ },
43
+
44
+ create: function(post) {
45
+ this.el.prepend(this.animate(this.render(post), 'bounceInLeft'));
46
+ },
47
+
48
+ update: function(post) {
49
+ var tmpl = this.render(post), row = this.find(post);
50
+ if (!row.length) { return this.create(post); }
51
+
52
+ this.animate(row, 'shake', function() {
53
+ row.replaceWith(tmpl);
54
+ });
55
+ },
56
+
57
+ destroy: function(post) {
58
+ var row = this.find(post);
59
+ this.animate(row, 'bounceOutRight', function(){ row.remove(); });
60
+ },
61
+
62
+ animate: function(el, name, callback) {
63
+ el.removeClass().addClass('animated ' + name);
64
+ return callback ? setTimeout(callback, 600) : el;
65
+ }
66
+ });
67
+
68
+ $(document).ready(function() {
69
+ new App('/events/posts', ['create', 'update', 'destroy']).start();
70
+ });
@@ -0,0 +1,32 @@
1
+ <html>
2
+ <head>
3
+ <title>Streaming Demo Page</title>
4
+ <link href="https://fonts.googleapis.com/css?family=Raleway:400,300,600" rel="stylesheet" type="text/css">
5
+ <link href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.2.6/animate.min.css" rel="stylesheet" type="text/css">
6
+ <link href="https://cdnjs.cloudflare.com/ajax/libs/skeleton/2.0.4/skeleton.min.css" rel="stylesheet" type="text/css">
7
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
8
+ <script src="./app.js"></script>
9
+ <style>
10
+ h1 { margin-top: 5rem; }
11
+ </style>
12
+ </head>
13
+
14
+ <body>
15
+ <div class="container">
16
+ <h1 class="title">Streaming Demo Page</h1>
17
+
18
+ <table class="u-full-width">
19
+ <thead>
20
+ <tr>
21
+ <th>#</th>
22
+ <th>Content</th>
23
+ <th>Number of updates</th>
24
+ <th>Last updated</th>
25
+ </tr>
26
+ </thead>
27
+
28
+ <tbody id="posts"></tbody>
29
+ </table>
30
+ </div>
31
+ </body>
32
+ </html>
@@ -0,0 +1,68 @@
1
+ require 'redis'
2
+ require 'active_record/publishable/version'
3
+
4
+ module ActiveRecord
5
+ module Publishable
6
+ class << self
7
+ attr_writer :redis
8
+
9
+ def included(base)
10
+ base.extend ClassMethods
11
+ end
12
+
13
+ def disable!
14
+ @disabled = true
15
+ end
16
+
17
+ def enable!
18
+ @disabled = nil
19
+ end
20
+
21
+ def disabled?
22
+ !!@disabled
23
+ end
24
+
25
+ def redis
26
+ @redis ||= Redis.new
27
+ end
28
+
29
+ def publish(channel, message)
30
+ redis.publish(channel, message) unless disabled?
31
+ end
32
+ end
33
+
34
+ module ClassMethods
35
+ def publishable(options = {})
36
+ Array(options.fetch(:on, [:create, :update, :destroy])).each do |verb|
37
+ after_commit options.merge(on: verb) do
38
+ unless ActiveRecord::Publishable.disabled?
39
+ publish_action(verb, options)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ def publish_action(action, options = {})
47
+ channel = options[:channel] || channel_for_publishing(action)
48
+ data = serialize_for_publishing(options.fetch(:serialize, {}))
49
+
50
+ ActiveRecord::Publishable.publish(channel, data.to_json)
51
+ end
52
+
53
+ def serialize_for_publishing(options = {})
54
+ opts = options.reverse_merge(root: false).except(:with)
55
+
56
+ serializer = options.fetch :with do
57
+ next unless defined? ActiveModel::Serializer
58
+ ActiveModel::Serializer.serializer_for self
59
+ end
60
+
61
+ serializer ? serializer.new(self, opts) : as_json(opts)
62
+ end
63
+
64
+ def channel_for_publishing(action)
65
+ "#{self.class.model_name.collection}:#{action}"
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,5 @@
1
+ module ActiveRecord
2
+ module Publishable
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1 @@
1
+ require 'active_record/publishable'
@@ -0,0 +1,146 @@
1
+ require 'spec_helper'
2
+
3
+ module ActiveRecord
4
+ describe Publishable do
5
+ describe '.disable!' do
6
+ after { Publishable.enable! }
7
+
8
+ it 'can be disabled' do
9
+ Publishable.enable!
10
+ Publishable.disable!
11
+ Publishable.disabled?.must_equal true
12
+ end
13
+ end
14
+
15
+ describe '.enable!' do
16
+ it 'can be re-enabled' do
17
+ Publishable.disable!
18
+ Publishable.enable!
19
+ Publishable.disabled?.must_equal false
20
+ end
21
+ end
22
+
23
+ describe '.redis=' do
24
+ it 'can be assigned' do
25
+ Publishable.redis = 'fake'
26
+ Publishable.redis.must_equal 'fake'
27
+ end
28
+ end
29
+
30
+ describe '.publish' do
31
+ let(:redis) { mock('Redis') }
32
+
33
+ before { Publishable.redis = redis }
34
+ after { Publishable.enable! }
35
+
36
+ it 'provides a convience publish method' do
37
+ redis.expects(:publish).at_least_once
38
+ Publishable.publish('channel', msg: 'yo')
39
+ end
40
+
41
+ it 'does nothing when Publishable.disabled?' do
42
+ Publishable.disable!
43
+
44
+ redis.expects(:publish).times(0)
45
+ Publishable.publish('channel', msg: 'yo')
46
+ end
47
+ end
48
+
49
+ describe 'ClassMethods' do
50
+ describe '.publishable' do
51
+ before do
52
+ Foo._commit_callbacks.clear
53
+ Foo.must_have_commit_callbacks 0
54
+ end
55
+
56
+ it 'should define default after_commit callbacks' do
57
+ Foo.publishable
58
+ Foo.must_have_commit_callbacks 3
59
+ end
60
+
61
+ it 'should allow specifying :on action' do
62
+ Foo.publishable on: [:create, :update]
63
+ Foo.must_have_commit_callbacks 2
64
+ end
65
+
66
+ it 'should allow :on to be a symbol' do
67
+ Foo.publishable on: :create
68
+ Foo.must_have_commit_callbacks 1
69
+ end
70
+
71
+ it 'should accept :if option' do
72
+ Foo.expects(:after_commit).with(on: :create, if: :something?)
73
+ Foo.publishable on: :create, if: :something?
74
+ end
75
+
76
+ it 'should accept :unless option' do
77
+ Foo.expects(:after_commit).with(on: :create, unless: :something?)
78
+ Foo.publishable on: :create, unless: :something?
79
+ end
80
+ end
81
+ end
82
+
83
+ let(:foo) { Foo.new name: 'foo' }
84
+ let(:bar) { Bar.new name: 'bar' }
85
+
86
+ describe '#publish_action' do
87
+ let(:channel) { foo.channel_for_publishing :create }
88
+ let(:data) { foo.serialize_for_publishing }
89
+
90
+ it 'should publish the event and data' do
91
+ Publishable.expects(:publish).with(channel, data.to_json)
92
+ foo.publish_action(:create)
93
+ end
94
+
95
+ it 'should pass :serialize option to #serialize_for_publishing' do
96
+ Publishable.stubs :publish
97
+ foo.expects(:serialize_for_publishing).with only: [:name]
98
+ foo.publish_action :create, serialize: { only: [:name] }
99
+ end
100
+
101
+ it 'should not do anything when disabled' do
102
+ Publishable.stubs(:disabled?).returns(true)
103
+ Publishable.expects(:redis).never
104
+ foo.publish_action :create
105
+ end
106
+ end
107
+
108
+ describe '#serialize_for_publishing' do
109
+ it 'should infer an active model serializer' do
110
+ foo.serialize_for_publishing.must_be_instance_of FooSerializer
111
+ end
112
+
113
+ it 'should use serializer from :with option' do
114
+ serialized = foo.serialize_for_publishing with: OtherFooSerializer
115
+ serialized.must_be_instance_of OtherFooSerializer
116
+ end
117
+
118
+ it 'should default to root: false element' do
119
+ FooSerializer.expects(:new).with foo, root: false
120
+ foo.serialize_for_publishing
121
+ end
122
+
123
+ it 'should pass :serialize option to the serializer' do
124
+ FooSerializer.expects(:new).with foo, root: 'foo', only: [:name]
125
+ foo.serialize_for_publishing root: 'foo', only: [:name]
126
+ end
127
+
128
+ it 'should serialize with as_json when serializer not found' do
129
+ bar.expects(:as_json).with root: false, only: [:name]
130
+ bar.serialize_for_publishing root: false, only: [:name]
131
+ end
132
+ end
133
+
134
+ describe '#channel_for_publishing' do
135
+ it 'should infer a reasonable default' do
136
+ foo.channel_for_publishing(:create).must_equal 'foos:create'
137
+ foo.channel_for_publishing(:update).must_equal 'foos:update'
138
+ foo.channel_for_publishing(:destroy).must_equal 'foos:destroy'
139
+ end
140
+
141
+ it 'should allow non-standard events' do
142
+ foo.channel_for_publishing(:custom).must_equal 'foos:custom'
143
+ end
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,44 @@
1
+ require 'bundler'
2
+ Bundler.require :default
3
+
4
+ require 'active_record'
5
+ require 'minitest/autorun'
6
+ require 'mocha/mini_test'
7
+
8
+ FileUtils.rm_rf File.expand_path('../test.sqlite3', __FILE__)
9
+ ActiveRecord::Base.establish_connection adapter: 'sqlite3', database: 'test.sqlite3'
10
+ ActiveRecord::Base.raise_in_transactional_callbacks = true
11
+
12
+ conn = ActiveRecord::Base.connection
13
+
14
+ [:foos, :bars].each do |table|
15
+ conn.create_table table, force: true do |t|
16
+ t.string :name
17
+ end
18
+ end
19
+
20
+ class Foo < ActiveRecord::Base
21
+ include ActiveRecord::Publishable
22
+ end
23
+
24
+ class Bar < ActiveRecord::Base
25
+ include ActiveRecord::Publishable
26
+ end
27
+
28
+ # Create some ActiveModel::Serializers (for integration testing)
29
+ class FooSerializer < ActiveModel::Serializer
30
+ attributes :id, :name
31
+ end
32
+
33
+ class OtherFooSerializer < FooSerializer; end
34
+
35
+
36
+ module MiniTest::Assertions
37
+ def assert_commit_callbacks(expected, klass)
38
+ assert_equal expected, klass._commit_callbacks.select { |cb|
39
+ cb.kind == :after && cb.name == :commit
40
+ }.size
41
+ end
42
+ end
43
+
44
+ Class.infect_an_assertion :assert_commit_callbacks, :must_have_commit_callbacks
metadata ADDED
@@ -0,0 +1,161 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: activerecord-publishable
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Ray Zane
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-10-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: redis
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activerecord
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: activesupport
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.7'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.7'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '10.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '10.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: minitest
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: mocha
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ description: Publish events to Redis when your models change.
112
+ email:
113
+ - raymondzane@gmail.com
114
+ executables: []
115
+ extensions: []
116
+ extra_rdoc_files: []
117
+ files:
118
+ - ".gitignore"
119
+ - Gemfile
120
+ - LICENSE.txt
121
+ - README.md
122
+ - Rakefile
123
+ - activerecord-publishable.gemspec
124
+ - examples/sse/app.rb
125
+ - examples/sse/change_data.rb
126
+ - examples/sse/post.rb
127
+ - examples/sse/public/app.js
128
+ - examples/sse/public/streaming.gif
129
+ - examples/sse/views/index.html.erb
130
+ - lib/active_record/publishable.rb
131
+ - lib/active_record/publishable/version.rb
132
+ - lib/activerecord-publishable.rb
133
+ - spec/active_record/publishable_spec.rb
134
+ - spec/spec_helper.rb
135
+ homepage: https://github.com/rzane/activerecord-publishable
136
+ licenses:
137
+ - MIT
138
+ metadata: {}
139
+ post_install_message:
140
+ rdoc_options: []
141
+ require_paths:
142
+ - lib
143
+ required_ruby_version: !ruby/object:Gem::Requirement
144
+ requirements:
145
+ - - ">="
146
+ - !ruby/object:Gem::Version
147
+ version: '0'
148
+ required_rubygems_version: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ requirements: []
154
+ rubyforge_project:
155
+ rubygems_version: 2.4.8
156
+ signing_key:
157
+ specification_version: 4
158
+ summary: Redis PubSub for ActiveRecord models.
159
+ test_files:
160
+ - spec/active_record/publishable_spec.rb
161
+ - spec/spec_helper.rb