active_projection 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d9052c28e84c5b64ab1b4350b291e02077f824ec
4
+ data.tar.gz: a7ad85d0ff185230ba7d350efa4a123143e60499
5
+ SHA512:
6
+ metadata.gz: b617e60a34666ba1fb02b110113c6d94a7b72c7f91d1c2a64d4272e44e7eec28894c4a4234491638b4babccae0cf1600a22089ce8630f7c9d3e33471c2662467
7
+ data.tar.gz: 1abea3c7eb6b8921ecbf7a4c3896642dd0ac55301439dd09b5013a51c4a12f9c8713b912f36004ac15d6aad45dff4751b48f4c4dec7eb4d6f39be76d747fd867
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2013 Andreas Reischuck
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # Active Projection
2
+
3
+ TODO: Write a gem description
@@ -0,0 +1,5 @@
1
+ module ActiveProjection
2
+ class Projection < ActiveRecord::Base
3
+ self.table_name = 'projections'
4
+ end
5
+ end
@@ -0,0 +1,43 @@
1
+ module ActiveProjection
2
+ class ProjectionRepository
3
+
4
+ def self.all
5
+ Projection.all
6
+ end
7
+
8
+ def self.get_last_id(id)
9
+ @@last_id[id] ||= Projection.find(id).last_id
10
+ end
11
+
12
+ def self.set_last_id(id, last_id)
13
+ Projection.find(id).update! last_id: last_id
14
+ @@last_id[id] += 1
15
+ end
16
+
17
+ def self.set_broken(id)
18
+ @@broken[id] = false
19
+ Projection.find(id).update! solid: false
20
+ end
21
+
22
+ def self.solid?(id)
23
+ @@broken[id] ||= Projection.find(id).solid
24
+ end
25
+
26
+ def self.create_or_get(projection_class)
27
+ projection = Projection.where(class_name: projection_class).first
28
+ if projection.nil?
29
+ projection = Projection.create! class_name: projection_class, last_id: 0, solid: true
30
+ else
31
+ projection.update! solid: true
32
+ end
33
+ projection
34
+ end
35
+
36
+ private
37
+ def self.initialize
38
+ end
39
+
40
+ @@last_id = {}
41
+ @@broken = {}
42
+ end
43
+ end
@@ -0,0 +1,10 @@
1
+ class CreateProjections < ActiveRecord::Migration
2
+ def change
3
+ create_table :projections do |t|
4
+ t.string :class_name
5
+ t.integer :last_id
6
+ t.boolean :solid
7
+ end
8
+ add_index :projections, :class_name
9
+ end
10
+ end
@@ -0,0 +1,16 @@
1
+ require 'active_event'
2
+ require 'active_support/core_ext'
3
+ require 'active_projection/version'
4
+
5
+ module ActiveProjection
6
+ extend ActiveSupport::Autoload
7
+
8
+ autoload :Autoload
9
+ autoload :EventClient
10
+ autoload :ProjectionType
11
+ autoload :ProjectionTypeRegistry
12
+ autoload :Server
13
+
14
+ autoload :Projection, (File.expand_path '../../app/models/active_projection/projection', __FILE__)
15
+ autoload :ProjectionRepository, (File.expand_path '../../app/models/active_projection/projection_repository', __FILE__)
16
+ end
@@ -0,0 +1,20 @@
1
+ module ActiveProjection
2
+ module Autoload
3
+ def self.worker_config=(config)
4
+ dirs = []
5
+ projections_dir = "#{config[:path]}**/*.rb"
6
+ if config[:count] == 1
7
+ dirs << projections_dir
8
+ else
9
+ Dir[*projections_dir].each_with_index.map { |item, i| i % config[:count] == config[:number] ? item : nil }.compact.each do |dir|
10
+ dirs << dir
11
+ end
12
+ end
13
+ ActiveEvent::Support::Autoloader.load_from dirs
14
+ end
15
+
16
+ def self.app_path=(path)
17
+ ActiveEvent::Support::Autoloader.load_from "#{path}/models/**/*.rb"
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,55 @@
1
+ require 'singleton'
2
+ require 'bunny'
3
+ module ActiveProjection
4
+ class EventClient
5
+ include Singleton
6
+
7
+ def self.start(options)
8
+ instance.start options
9
+ end
10
+
11
+ def start(options)
12
+ self.options = options
13
+ event_connection.start
14
+ begin
15
+ event_channel.queue('', auto_delete: true).bind(event_exchange).subscribe do |delivery_info, properties, body|
16
+ puts "Received #{properties.type} with #{body}"
17
+ ProjectionTypeRegistry.process(properties.headers.deep_symbolize_keys, Object.const_get(properties.type).new((JSON.parse body).deep_symbolize_keys))
18
+ end
19
+ rescue Interrupt => _
20
+ event_channel.close
21
+ event_connection.close
22
+ end
23
+ last_ids = ProjectionRepository.all.to_a.map { |p| p.last_id }
24
+ request_events_after last_ids.min || 0
25
+ event_channel.work_pool.join
26
+ end
27
+
28
+ def request_events_after(id)
29
+ resend_exchange.publish id.to_s, routing_key: 'resend'
30
+ end
31
+
32
+ def event_connection
33
+ @event_server ||= Bunny.new URI::Generic.build(options[:event_connection]).to_s
34
+ end
35
+
36
+ def event_channel
37
+ @event_channel ||= event_connection.create_channel
38
+ end
39
+
40
+ def event_exchange
41
+ @event_exchange ||= event_channel.fanout options[:event_exchange]
42
+ end
43
+
44
+ def resend_exchange
45
+ @resend_exchange ||= event_channel.direct "resend_#{options[:event_exchange]}"
46
+ end
47
+
48
+ def options
49
+ @options
50
+ end
51
+
52
+ private
53
+ attr_writer :options
54
+ end
55
+ end
@@ -0,0 +1,56 @@
1
+ module ActiveProjection
2
+ module ProjectionType
3
+ extend ActiveSupport::Concern
4
+ class WrongArgumentsCountError < StandardError
5
+ end
6
+ included do
7
+ ProjectionTypeRegistry::register(self)
8
+ end
9
+
10
+ def initialize
11
+ self.handlers = Hash.new do |hash, key|
12
+ hash[key] = []
13
+ end
14
+ self.class.public_instance_methods(false).each do |method_name|
15
+ method = self.class.instance_method method_name
16
+ event_type = method_name.to_s.camelcase.to_sym
17
+ raise WrongArgumentsCountError if 1 != method.arity
18
+ handlers[event_type] << method.name
19
+ end
20
+ end
21
+
22
+ def evaluate(headers)
23
+ return false unless ProjectionRepository.solid? projection_id #projection is broken and shouldn't process any incoming event
24
+ last_id = ProjectionRepository.get_last_id projection_id
25
+ case
26
+ when last_id + 1 == headers[:store_id] #process incoming event
27
+ ProjectionRepository.set_last_id projection_id, headers[:store_id]
28
+ puts "process this event"
29
+ true
30
+ when last_id >= headers[:store_id] #incoming event already processed
31
+ puts "event already processed"
32
+ false
33
+ when last_id < headers[:store_id] #incoming event is in future, so we are missing some
34
+ puts "events are missing"
35
+ ProjectionRepository.set_broken projection_id
36
+ false
37
+ end
38
+ end
39
+
40
+ def invoke(event)
41
+ event_type = event.class.name.split('::').last.to_sym
42
+ return unless handlers.has_key? event_type
43
+ handlers[event_type].each do |method|
44
+ self.send method, event
45
+ end
46
+ end
47
+
48
+ private
49
+ attr_accessor :handlers
50
+ attr_writer :projection_id
51
+
52
+ def projection_id
53
+ @projection_id ||= ProjectionRepository.create_or_get(self.class.name).id
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,38 @@
1
+ require 'singleton'
2
+
3
+ module ActiveProjection
4
+ class ProjectionTypeRegistry
5
+ include Singleton
6
+
7
+ def self.register(projection)
8
+ self.registry << projection
9
+ end
10
+
11
+ def self.process(headers, event)
12
+ instance.process headers, event
13
+ end
14
+
15
+ def process(headers, event)
16
+ projections.each do |projection|
17
+ ActiveRecord::Base.transaction do
18
+ projection.invoke event if projection.evaluate headers
19
+ end
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ cattr_accessor :registry
26
+ attr_accessor :projections
27
+
28
+ def initialize
29
+ self.projections = []
30
+ self.class.registry.freeze.each do |projection|
31
+ projections << projection.new
32
+ end
33
+ projections.freeze
34
+ end
35
+
36
+ self.registry = []
37
+ end
38
+ end
@@ -0,0 +1,8 @@
1
+ require 'active_projection'
2
+ require 'rails'
3
+
4
+ module ActiveProjection
5
+ class Railtie < Rails::Railtie # :nodoc:
6
+ config.eager_load_namespaces << ActiveProjection
7
+ end
8
+ end
@@ -0,0 +1,60 @@
1
+ require 'active_record'
2
+
3
+ module ActiveProjection
4
+ class Server
5
+
6
+ def self.run(options = nil)
7
+ self.new(options).run
8
+ end
9
+
10
+ def initialize(new_options = nil)
11
+ self.options = new_options.deep_symbolize_keys! unless new_options.nil?
12
+ end
13
+
14
+ def run
15
+ ActiveRecord::Base.establish_connection options[:projection_database]
16
+ EventClient.start options
17
+ end
18
+
19
+ def env
20
+ @env = ENV['PROJECTION_ENV'] || ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development'
21
+ end
22
+
23
+ def options
24
+ @options ||= parse_options(ARGV)
25
+ end
26
+
27
+ cattr_accessor :base_path
28
+ cattr_accessor :config_file
29
+
30
+ def config_file
31
+ self.class.config_file || File.expand_path('config/disco.yml', base_path)
32
+ end
33
+
34
+ private
35
+
36
+ attr_writer :options
37
+ attr_writer :domain
38
+
39
+ def default_options
40
+ {
41
+ projection_database: {
42
+ adapter: 'sqlite3',
43
+ database: File.expand_path('db/projection.sqlite3', base_path)
44
+ },
45
+ event_connection: {
46
+ scheme: 'amqp',
47
+ userinfo: nil,
48
+ host: '127.0.0.1',
49
+ port: 9797,
50
+ },
51
+ event_exchange: 'events'
52
+ }
53
+ end
54
+
55
+ def parse_options(args)
56
+ options = default_options
57
+ options.merge! YAML.load_file(config_file)[env].deep_symbolize_keys! unless config_file.blank?
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,11 @@
1
+ module ActiveProjection
2
+ # Returns the version of the currently loaded ActiveProjection as a Gem::Version
3
+ def self.version
4
+ Gem::Version.new '0.1.0'
5
+ end
6
+
7
+ module VERSION #:nodoc:
8
+ MAJOR, MINOR, TINY, PRE = ActiveProjection.version.segments
9
+ STRING = ActiveProjection.version.to_s
10
+ end
11
+ end
@@ -0,0 +1,8 @@
1
+ require 'factory_girl'
2
+
3
+ FactoryGirl.define do
4
+ factory :projection, class: ActiveProjection::Projection do
5
+ last_id 4
6
+ solid true
7
+ end
8
+ end
@@ -0,0 +1,22 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe ActiveProjection::ProjectionTypeRegistry do
4
+ class TestEvent
5
+ end
6
+ class TestProjection
7
+ end
8
+ before :all do
9
+ ActiveProjection::ProjectionTypeRegistry.register(TestProjection)
10
+ end
11
+
12
+ it 'initializes' do
13
+ ActiveProjection::ProjectionTypeRegistry.instance.should be
14
+ end
15
+
16
+ it 'invokes event handlers' do
17
+ event = TestEvent.new
18
+ expect_any_instance_of(TestProjection).to receive(:evaluate).with('test').and_return(true)
19
+ expect_any_instance_of(TestProjection).to receive(:invoke).with(event)
20
+ ActiveProjection::ProjectionTypeRegistry.process('test', event)
21
+ end
22
+ end
@@ -0,0 +1,74 @@
1
+ require_relative '../spec_helper'
2
+ require_relative '../support/active_record'
3
+
4
+ describe ActiveProjection::ProjectionType do
5
+ it 'should register automatically' do
6
+ expect(ActiveProjection::ProjectionTypeRegistry).to receive(:register) do |arg|
7
+ arg.name.to_sym.should eq :DummyProjection
8
+ end
9
+ class DummyProjection
10
+ include ActiveProjection::ProjectionType
11
+ end
12
+ end
13
+ describe 'instance' do
14
+ class TestProjection
15
+ def test_event(event)
16
+ end
17
+
18
+ def dummy_event(event)
19
+ end
20
+ end
21
+ class TestEvent
22
+ end
23
+ before :each do
24
+ allow(ActiveProjection::ProjectionTypeRegistry).to receive(:register)
25
+ TestProjection.send(:include, ActiveProjection::ProjectionType)
26
+ @projection = TestProjection.new
27
+ end
28
+ it 'initializes all handler' do
29
+ handlers = @projection.instance_variable_get(:@handlers)
30
+ handlers.length.should eq 2
31
+ handlers['TestEvent'].should be
32
+ handlers['DummyEvent'].should be
33
+ end
34
+
35
+ describe 'evaluate' do
36
+ before :each do
37
+ allow(@projection).to receive(:projection_id).and_return(0)
38
+ end
39
+
40
+ it 'rejects processing, if projection is broken' do
41
+ expect(ActiveProjection::ProjectionRepository).to receive(:solid?).and_return(false)
42
+ expect(ActiveProjection::ProjectionRepository).to_not receive(:get_last_id)
43
+ @projection.evaluate({store_id: 1})
44
+ end
45
+
46
+ describe 'with solid projection' do
47
+ before :each do
48
+ allow(ActiveProjection::ProjectionRepository).to receive(:solid?).and_return(true)
49
+ expect(ActiveProjection::ProjectionRepository).to receive(:get_last_id).and_return(5)
50
+ end
51
+
52
+ it 'processes event with the correct id' do
53
+ expect(ActiveProjection::ProjectionRepository).to receive(:set_last_id).with(0, 6)
54
+ expect(@projection.evaluate({store_id: 6})).to be_true
55
+ end
56
+
57
+ it 'rejects event with last id smaller event id' do
58
+ expect(@projection.evaluate({store_id: 3})).to be_false
59
+ end
60
+
61
+ it 'rejects event with last greater equal event id' do
62
+ expect(ActiveProjection::ProjectionRepository).to receive(:set_broken).with(0)
63
+ expect(@projection.evaluate({store_id: 7})).to be_false
64
+ end
65
+ end
66
+ end
67
+
68
+ it 'invokes all handler' do
69
+ expect(@projection).to receive(:test_event)
70
+ expect(@projection).to_not receive(:dummy_event)
71
+ @projection.invoke(TestEvent.new)
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,27 @@
1
+ require_relative '../spec_helper'
2
+ require_relative '../support/active_record'
3
+
4
+ describe ActiveProjection::ProjectionRepository do
5
+ before :all do
6
+ 5.times do |i|
7
+ FactoryGirl.create :projection, id: i, class_name: "TestProjection#{i}"
8
+ end
9
+ end
10
+ describe 'create_or_get' do
11
+ it 'creates a projection, if classname is not present' do
12
+ expect(ActiveProjection::ProjectionRepository.create_or_get('TestProjection5').id).to eq 5
13
+ expect(ActiveProjection::Projection.all.to_a.length).to eq 6
14
+ end
15
+
16
+ it 'does not create a projection, if classname is present' do
17
+ expect(ActiveProjection::ProjectionRepository.create_or_get('TestProjection3').id).to eq 3
18
+ expect(ActiveProjection::Projection.all.to_a.length).to eq 5
19
+ end
20
+ end
21
+ describe 'broken' do
22
+ it 'sets solid to false' do
23
+ ActiveProjection::ProjectionRepository.set_broken 0
24
+ expect(ActiveProjection::Projection.where(id:0).first.solid).to be_false
25
+ end
26
+ end
27
+ end
@@ -0,0 +1 @@
1
+ require 'active_projection'
@@ -0,0 +1,38 @@
1
+
2
+ require 'active_record'
3
+
4
+ ActiveRecord::Base.establish_connection adapter: 'sqlite3', database: ':memory:'
5
+
6
+ ActiveRecord::Migrator.up 'db/migrate'
7
+
8
+ module ActiveModel::Validations
9
+ # Extension to enhance `should have` on AR Model instances. Calls
10
+ # model.valid? in order to prepare the object's errors object.
11
+ #
12
+ # You can also use this to specify the content of the error messages.
13
+ #
14
+ # @example
15
+ #
16
+ # model.should have(:no).errors_on(:attribute)
17
+ # model.should have(1).error_on(:attribute)
18
+ # model.should have(n).errors_on(:attribute)
19
+ #
20
+ # model.errors_on(:attribute).should include("can't be blank")
21
+ def errors_on(attribute)
22
+ self.valid?
23
+ [self.errors[attribute]].flatten.compact
24
+ end
25
+ alias :error_on :errors_on
26
+ end
27
+
28
+ RSpec.configure do |config|
29
+ config.around do |example|
30
+ ActiveRecord::Base.transaction do
31
+ example.run
32
+ raise ActiveRecord::Rollback
33
+ end
34
+ end
35
+ end
36
+
37
+ require 'factory_girl'
38
+ FactoryGirl.find_definitions
metadata ADDED
@@ -0,0 +1,206 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: active_projection
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Andreas Reischuck
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-09-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: active_event
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '='
18
+ - !ruby/object:Gem::Version
19
+ version: 0.1.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.1.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: 3.2.0
34
+ - - <
35
+ - !ruby/object:Gem::Version
36
+ version: 4.1.0
37
+ type: :runtime
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - '>'
42
+ - !ruby/object:Gem::Version
43
+ version: 3.2.0
44
+ - - <
45
+ - !ruby/object:Gem::Version
46
+ version: 4.1.0
47
+ - !ruby/object:Gem::Dependency
48
+ name: activemodel
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - '>'
52
+ - !ruby/object:Gem::Version
53
+ version: 3.2.0
54
+ - - <
55
+ - !ruby/object:Gem::Version
56
+ version: 4.1.0
57
+ type: :runtime
58
+ prerelease: false
59
+ version_requirements: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - '>'
62
+ - !ruby/object:Gem::Version
63
+ version: 3.2.0
64
+ - - <
65
+ - !ruby/object:Gem::Version
66
+ version: 4.1.0
67
+ - !ruby/object:Gem::Dependency
68
+ name: bunny
69
+ requirement: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - '>='
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ type: :runtime
75
+ prerelease: false
76
+ version_requirements: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - '>='
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ - !ruby/object:Gem::Dependency
82
+ name: bundler
83
+ requirement: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ~>
86
+ - !ruby/object:Gem::Version
87
+ version: '1.3'
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ~>
93
+ - !ruby/object:Gem::Version
94
+ version: '1.3'
95
+ - !ruby/object:Gem::Dependency
96
+ name: rake
97
+ requirement: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - '>='
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ - !ruby/object:Gem::Dependency
110
+ name: rspec
111
+ requirement: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - '>='
114
+ - !ruby/object:Gem::Version
115
+ version: '0'
116
+ type: :development
117
+ prerelease: false
118
+ version_requirements: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - '>='
121
+ - !ruby/object:Gem::Version
122
+ version: '0'
123
+ - !ruby/object:Gem::Dependency
124
+ name: factory_girl
125
+ requirement: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - '>='
128
+ - !ruby/object:Gem::Version
129
+ version: '0'
130
+ type: :development
131
+ prerelease: false
132
+ version_requirements: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - '>='
135
+ - !ruby/object:Gem::Version
136
+ version: '0'
137
+ - !ruby/object:Gem::Dependency
138
+ name: sqlite3
139
+ requirement: !ruby/object:Gem::Requirement
140
+ requirements:
141
+ - - '>='
142
+ - !ruby/object:Gem::Version
143
+ version: '0'
144
+ type: :development
145
+ prerelease: false
146
+ version_requirements: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - '>='
149
+ - !ruby/object:Gem::Version
150
+ version: '0'
151
+ description: Projection environment for rails-disco
152
+ email: andreas.reischuck@hicknhack-software.com
153
+ executables: []
154
+ extensions: []
155
+ extra_rdoc_files: []
156
+ files:
157
+ - app/models/active_projection/projection.rb
158
+ - app/models/active_projection/projection_repository.rb
159
+ - db/migrate/01_create_projections.rb
160
+ - lib/active_projection/autoload.rb
161
+ - lib/active_projection/event_client.rb
162
+ - lib/active_projection/projection_type.rb
163
+ - lib/active_projection/projection_type_registry.rb
164
+ - lib/active_projection/railtie.rb
165
+ - lib/active_projection/server.rb
166
+ - lib/active_projection/version.rb
167
+ - lib/active_projection.rb
168
+ - README.md
169
+ - MIT-LICENSE
170
+ - spec/factories/event_factory.rb
171
+ - spec/lib/projection_type_registry_spec.rb
172
+ - spec/lib/projecton_type_spec.rb
173
+ - spec/models/projection_repository_spec.rb
174
+ - spec/spec_helper.rb
175
+ - spec/support/active_record.rb
176
+ homepage: https://github.com/hicknhack-software/rails-disco
177
+ licenses:
178
+ - MIT
179
+ metadata: {}
180
+ post_install_message:
181
+ rdoc_options: []
182
+ require_paths:
183
+ - lib
184
+ required_ruby_version: !ruby/object:Gem::Requirement
185
+ requirements:
186
+ - - '>='
187
+ - !ruby/object:Gem::Version
188
+ version: '0'
189
+ required_rubygems_version: !ruby/object:Gem::Requirement
190
+ requirements:
191
+ - - '>='
192
+ - !ruby/object:Gem::Version
193
+ version: '0'
194
+ requirements: []
195
+ rubyforge_project:
196
+ rubygems_version: 2.0.5
197
+ signing_key:
198
+ specification_version: 4
199
+ summary: Projection related stuff (part of rails-disco)
200
+ test_files:
201
+ - spec/factories/event_factory.rb
202
+ - spec/lib/projection_type_registry_spec.rb
203
+ - spec/lib/projecton_type_spec.rb
204
+ - spec/models/projection_repository_spec.rb
205
+ - spec/spec_helper.rb
206
+ - spec/support/active_record.rb