merging-queue 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: 7dae3e92ad88cafbb75132665eae4f3207052388
4
+ data.tar.gz: b1feaf6f3cc0ec5e0f8bd35e9ae42b3682fe5494
5
+ SHA512:
6
+ metadata.gz: fd9822d92b529e05891f578404fe341a102eb2a921f933588f51a484ab70b52ea95418ed3c410107cf21dc1fb2031d034c11cfb7abfb676674e9a8d2a7fe26f5
7
+ data.tar.gz: 8250336736f7c5621b30d69d47f0b8f43dd3657c4c5c251160a0a62ea38091f925e36550a992e4fca9a06c83be296384b168b685d62606a2d0f442401d671fda
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ services: SQLite3
3
+ gemfile:
4
+ - Gemfile
5
+ rvm:
6
+ - 1.9.3
7
+ - 2.0.0
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source "http://rubygems.org"
2
+
3
+ group :development, :test do
4
+ gem "sqlite3", :platform => [:ruby, :mswin, :mingw]
5
+ gem "jdbc-sqlite3", :platform => :jruby
6
+ end
7
+
8
+ # Specify your gem's dependencies in merging-queue.gemspec
9
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2013 Andreas Saebjoernsen
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,146 @@
1
+ # MergingQueue
2
+
3
+ MergingQueue is a simple gem for grouping tasks by type and time using the ActiveRecord ODM framework and
4
+ background jobs.
5
+
6
+ This gem is inspired by Streama by Christopher Pappas.
7
+
8
+ [![Build Status](https://secure.travis-ci.org/digitalplaywright/merging-queue.png)](http://travis-ci.org/digitalplaywright/merging-queue) [![Dependency Status](https://gemnasium.com/digitalplaywright/merging-queue.png)](https://gemnasium.com/digitalplaywright/merging-queue) [![Code Climate](https://codeclimate.com/github/digitalplaywright/merging-queue.png)](https://codeclimate.com/github/digitalplaywright/merging-queue)
9
+
10
+
11
+ ## Install
12
+
13
+ gem install merging-queue
14
+
15
+ ## Usage
16
+
17
+ ### Create migration for queued_tasks and migrate the database (in your Rails project):
18
+
19
+ ```ruby
20
+ rails g merging-queue:migration
21
+ rake db:migrate
22
+ ```
23
+
24
+ ### Define Queued Task
25
+
26
+ Create an QueuedTask model and define the queued_tasks and the fields you would like to cache within the queued_task.
27
+
28
+ An queued_task consists of an actor, a verb, an act_object, and a target.
29
+
30
+ ``` ruby
31
+ class QueuedTask < ActiveRecord::Base
32
+ include MergingQueue::QueuedTask
33
+
34
+ queued_task :new_enquiry do
35
+ actor :User
36
+ act_object :Article
37
+ act_target :Volume
38
+ end
39
+ end
40
+ ```
41
+
42
+ The queued_task verb is implied from the queued_task name, in the above example the verb is :new_enquiry
43
+
44
+ The act_object may be the entity performing the queued_task, or the entity on which the queued_task was performed.
45
+ e.g John(actor) shared a video(act_object)
46
+
47
+ The target is the act_object that the verb is enacted on.
48
+ e.g. Geraldine(actor) posted a photo(act_object) to her album(target)
49
+
50
+ This is based on the QueuedTask Streams 1.0 specification (http://queued_taskstrea.ms)
51
+
52
+ ### Setup Actors
53
+
54
+ Include the Actor module in a class and override the default followers method.
55
+
56
+ ``` ruby
57
+ class User < ActiveRecord::Base
58
+ include MergingQueue::Actor
59
+
60
+ end
61
+ ```
62
+
63
+
64
+
65
+ ### Publishing QueuedTask
66
+
67
+ In your controller or background worker:
68
+
69
+ ``` ruby
70
+ current_user.publish_queued_task(:new_enquiry, :act_object => @enquiry, :target => @listing)
71
+ ```
72
+
73
+ This will publish the queued_task to the mongoid act_objects returned by the #followers method in the Actor.
74
+
75
+
76
+ ## Retrieving QueuedTask
77
+
78
+ To retrieve all queued_task for an actor
79
+
80
+ ``` ruby
81
+ current_user.queued_tasks
82
+ ```
83
+
84
+ To retrieve and filter to a particular queued_task type
85
+
86
+ ``` ruby
87
+ current_user.queued_tasks(:verb => 'new_enquiry')
88
+ ```
89
+
90
+ #### Options
91
+
92
+ Additional options can be required:
93
+
94
+ ``` ruby
95
+ class QueuedTask < ActiveRecord::Base
96
+ include MergingQueue::QueuedTask
97
+
98
+ queued_task :new_enquiry do
99
+ actor :User
100
+ act_object :Article
101
+ act_target :Volume
102
+ option :country
103
+ option :city
104
+ end
105
+ end
106
+ ```
107
+
108
+ The option fields are stored using the ActiveRecord 'store' feature.
109
+
110
+
111
+ #### Bond type
112
+
113
+ A verb can have one bond type. This bond type can be used to classify and quickly retrieve
114
+ queued_task feed items that belong to a particular aggregate feed, like e.g the global feed.
115
+
116
+ ``` ruby
117
+ class QueuedTask < ActiveRecord::Base
118
+ include MergingQueue::QueuedTask
119
+
120
+ queued_task :new_enquiry do
121
+ actor :User
122
+ act_object :Article
123
+ act_target :Volume
124
+ bond_type :global
125
+ end
126
+ end
127
+ ```
128
+
129
+ ### Poll for changes and empty queue
130
+
131
+ There is a poll changes interface that will group tasks by first actor and then verb.
132
+
133
+ ```ruby
134
+ QueuedTask.poll_for_changes() do |verb, hash|
135
+ #verb is the verb the changes are group by
136
+ #hash has format: {:actor => _actor /* actor is the */,
137
+ # :act_objects => act_objects /* all objects from matching tasks */,
138
+ # :act_object_ids => act_object_ids /* all object ids from matching tasks */,
139
+ # :act_targets => act_targets /* all targets from matching tasks */ ,
140
+ # :act_target_ids => act_target_ids /* all target ids from matching tasks}
141
+ end
142
+ ```
143
+
144
+
145
+
146
+
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake'
3
+ require 'rake/testtask'
4
+
5
+ task :default => :test
6
+
7
+ Rake::TestTask.new do |t|
8
+ t.libs << "test"
9
+ t.test_files = FileList['test/test*.rb', 'test/unit/*_test.rb']
10
+ end
11
+
@@ -0,0 +1,14 @@
1
+ require 'rails/generators/named_base'
2
+
3
+ module MergingQueue
4
+ # A generator module with QueuedTask table schema.
5
+ module Generators
6
+ # A base module
7
+ module Base
8
+ # Get path for migration template
9
+ def source_root
10
+ @_merging-queue_source_root ||= File.expand_path(File.join('../merging-queue', generator_name, 'templates'), __FILE__)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,17 @@
1
+ require 'generators/queue_collapser'
2
+ require 'rails/generators/active_record'
3
+
4
+ module MergingQueue
5
+ module Generators
6
+ # QueuedTask generator that creates queued_task model file from template
7
+ class QueuedTaskGenerator < ActiveRecord::Generators::Base
8
+ extend Base
9
+
10
+ argument :name, :type => :string, :default => 'queued_task'
11
+ # Create model in project's folder
12
+ def generate_files
13
+ copy_file 'queued_task.rb', "app/models/#{name}.rb"
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,5 @@
1
+ # QueuedTask model for customisation & custom methods
2
+ class QueuedTask < ActiveRecord::Base
3
+ include MergingQueue::QueuedTask
4
+
5
+ end
@@ -0,0 +1,17 @@
1
+ require 'generators/queue_collapser'
2
+ require 'rails/generators/active_record'
3
+
4
+ module LiveStream
5
+ module Generators
6
+ # Migration generator that creates migration file from template
7
+ class MigrationGenerator < ActiveRecord::Generators::Base
8
+ extend Base
9
+
10
+ argument :name, :type => :string, :default => 'create_queued_tasks'
11
+ # Create migration in project's folder
12
+ def generate_files
13
+ migration_template 'migration.rb', "db/migrate/#{name}"
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,30 @@
1
+ # Migration responsible for creating a table with queued_tasks
2
+ class CreateQueuedTasks < ActiveRecord::Migration
3
+ # Create table
4
+ def self.up
5
+ create_table :queued_tasks do |t|
6
+ t.belongs_to :actor, :polymorphic => true
7
+ t.belongs_to :act_object, :polymorphic => true
8
+ t.belongs_to :act_target, :polymorphic => true
9
+
10
+ t.string :verb
11
+ t.string :description
12
+ t.string :options
13
+ t.string :bond_type
14
+
15
+ t.time :publish_on
16
+ t.string :state
17
+
18
+ t.timestamps
19
+ end
20
+
21
+ add_index :queued_tasks, [:verb]
22
+ add_index :queued_tasks, [:actor_id, :actor_type]
23
+ add_index :queued_tasks, [:act_object_id, :act_object_type]
24
+ add_index :queued_tasks, [:act_target_id, :act_target_type]
25
+ end
26
+ # Drop table
27
+ def self.down
28
+ drop_table :queued_tasks
29
+ end
30
+ end
@@ -0,0 +1,9 @@
1
+ require 'active_record'
2
+ require 'active_support'
3
+ require 'active_support/core_ext/module/delegation.rb'
4
+ require "merging-queue/version"
5
+ require "merging-queue/actor"
6
+ require "merging-queue/queued_task"
7
+ require "merging-queue/definition"
8
+ require "merging-queue/definition_dsl"
9
+ require "merging-queue/errors"
@@ -0,0 +1,54 @@
1
+ module MergingQueue
2
+
3
+ module Actor
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ cattr_accessor :queued_task_klass
8
+
9
+ has_many :queued_tasks, :class_name => "QueuedTask", :as => :actor
10
+ has_many :act_object_queued_tasks, :class_name => "QueuedTask", :as => :act_object
11
+ has_many :act_target_queued_tasks, :class_name => "QueuedTask", :as => :act_target
12
+
13
+
14
+ end
15
+
16
+ module ClassMethods
17
+
18
+ def queued_task_class(klass)
19
+ self.queued_task_klass = klass.to_s
20
+ end
21
+
22
+ end
23
+
24
+
25
+ # Publishes the queued_task to the receivers
26
+ #
27
+ # @param [ Hash ] options The options to publish with.
28
+ #
29
+ # @example publish an queued_task with a act_object and act_target
30
+ # current_user.publish_queued_task(:enquiry, :act_object => @enquiry, :act_target => @listing)
31
+ #
32
+ def publish_queued_task(name, cur_publish_on, options={})
33
+ cur_publish_on = Time.now + cur_publish_on if cur_publish_on.kind_of?(Fixnum)
34
+ raise "Expected Time type. Got:" + cur_publish_on.class.name unless cur_publish_on.kind_of?(Time)
35
+ queued_task_class.publish(name, cur_publish_on, {:actor => self}.merge(options))
36
+ end
37
+
38
+ def queued_task_class
39
+ @queued_task_klass ||= queued_task_klass ? queued_task_klass.classify.constantize : ::QueuedTask
40
+ end
41
+
42
+ def queued_merging_tasks(options = {})
43
+
44
+ if options.empty?
45
+ queued_tasks
46
+ else
47
+ queued_tasks.where(options)
48
+ end
49
+
50
+ end
51
+
52
+ end
53
+
54
+ end
@@ -0,0 +1,48 @@
1
+ module MergingQueue
2
+
3
+ class Definition
4
+
5
+ attr_reader :name, :actor, :act_object, :act_target, :grouped_actor, :bond_type, :options
6
+
7
+ # @param dsl [MergingQueue::DefinitionDSL] A DSL act_object
8
+ def initialize(definition)
9
+ @name = definition[:name]
10
+ @actor = definition[:actor] || nil
11
+ @act_object = definition[:act_object] || nil
12
+ @act_target = definition[:act_target] || nil
13
+ @grouped_actor = definition[:grouped_actor] || nil
14
+ @bond_type = definition[:bond_type] || nil
15
+ @options = definition[:options] || []
16
+ end
17
+
18
+ #
19
+ # Registers a new definition
20
+ #
21
+ # @param definition [Definition] The definition to register
22
+ # @return [Definition] Returns the registered definition
23
+ def self.register(definition)
24
+ return false unless definition.is_a? DefinitionDSL
25
+ definition = new(definition)
26
+ self.registered << definition
27
+ return definition || false
28
+ end
29
+
30
+ # List of registered definitions
31
+ # @return [Array<MergingQueue::Definition>]
32
+ def self.registered
33
+ @definitions ||= []
34
+ end
35
+
36
+ def self.find(name)
37
+ unless definition = registered.find{|definition| definition.name == name.to_sym}
38
+ raise MergingQueue::InvalidQueuedTask, "Could not find a definition for `#{name}`"
39
+ else
40
+ definition
41
+ end
42
+ end
43
+
44
+
45
+
46
+ end
47
+
48
+ end
@@ -0,0 +1,48 @@
1
+ require 'active_support'
2
+
3
+ module MergingQueue
4
+
5
+ class DefinitionDSL
6
+
7
+ attr_reader :attributes
8
+
9
+ def initialize(name)
10
+ @attributes = {
11
+ :name => name.to_sym,
12
+ :actor => nil,
13
+ :act_object => nil,
14
+ :act_target => nil,
15
+ :grouped_actor => nil,
16
+ :reverses => nil,
17
+ :bond_type => nil,
18
+ :options => nil
19
+ }
20
+ end
21
+
22
+ def add_option(option)
23
+ @attributes[:options] ||= []
24
+
25
+ @attributes[:options] << option
26
+ end
27
+
28
+ def option(text)
29
+ add_option( text )
30
+ end
31
+
32
+ delegate :[], :to => :@attributes
33
+
34
+ def self.data_methods(*args)
35
+ args.each do |method|
36
+ define_method method do |*args|
37
+
38
+ @attributes[method] = args[0]
39
+
40
+ end
41
+ end
42
+ end
43
+
44
+ data_methods :actor, :act_object, :act_target, :grouped_actor, :bond_type
45
+
46
+ end
47
+
48
+ end
@@ -0,0 +1,44 @@
1
+ module MergingQueue
2
+
3
+ class MergingQueueError < StandardError
4
+ end
5
+
6
+ class InvalidQueuedTask < MergingQueueError
7
+ end
8
+
9
+ # This error is raised when an act_object isn't defined
10
+ # as an actor, act_object or act_target
11
+ #
12
+ # Example:
13
+ #
14
+ # <tt>InvalidField.new('field_name')</tt>
15
+ class InvalidData < MergingQueueError
16
+ attr_reader :message
17
+
18
+ def initialize message
19
+ @message = "Invalid Data: #{message}"
20
+ end
21
+
22
+ end
23
+
24
+ # This error is raised when trying to store a field that doesn't exist
25
+ #
26
+ # Example:
27
+ #
28
+ # <tt>InvalidField.new('field_name')</tt>
29
+ class InvalidField < MergingQueueError
30
+ attr_reader :message
31
+
32
+ def initialize message
33
+ @message = "Invalid Field: #{message}"
34
+ end
35
+
36
+ end
37
+
38
+ class QueuedTaskNotSaved < MergingQueueError
39
+ end
40
+
41
+ class NoFollowersDefined < MergingQueueError
42
+ end
43
+
44
+ end
@@ -0,0 +1,237 @@
1
+ module MergingQueue
2
+ module QueuedTask
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+
7
+ store :options
8
+
9
+ belongs_to :actor, :polymorphic => true
10
+ belongs_to :act_object, :polymorphic => true
11
+ belongs_to :act_target, :polymorphic => true
12
+
13
+ validates_presence_of :actor_id, :actor_type, :verb
14
+ end
15
+
16
+ module ClassMethods
17
+
18
+ def poll_for_changes()
19
+ #if block_given?
20
+
21
+ ::QueuedTask.where('state = ? AND publish_on <= ?', 'initial', Time.now).distinct(:actor_id).each do |actor_id|
22
+
23
+ ::QueuedTask.where('state = ? AND actor_id = ? AND publish_on <= ?', 'initial', actor_id, Time.now).select(:verb).uniq.each do |verb|
24
+
25
+ changes_for_actor = ::QueuedTask.where(:actor_id => actor_id, :state => 'initial', :verb => verb['verb'].to_s)
26
+
27
+ begin
28
+ yield verb, accumulate_changes(changes_for_actor)
29
+
30
+ changes_for_actor.destroy_all
31
+
32
+ rescue ActiveRecord::RecordNotFound
33
+ end
34
+
35
+
36
+ end
37
+
38
+
39
+ end
40
+ end
41
+
42
+ #---- general helpers
43
+
44
+ #---- add up all actors, act_objects and act_targets in the search results.
45
+ # it does not matter what is where
46
+ def accumulate_changes(search_result)
47
+ act_targets = []
48
+ act_target_ids = []
49
+ act_objects = []
50
+ act_object_ids = []
51
+
52
+ _actor = search_result.first.actor
53
+
54
+ search_result.each do |result|
55
+ result.update_attributes(:state => 'pending')
56
+
57
+ act_object = result.act_object
58
+ act_target = result.act_target
59
+
60
+ if act_object != nil && !act_object_ids.include?(act_object.id)
61
+ act_objects << act_object
62
+ act_object_ids << act_object.id
63
+
64
+ end
65
+
66
+ if act_target != nil && !act_target_ids.include?(act_target.id)
67
+ act_targets << act_target
68
+ act_target_ids << act_target.id
69
+
70
+ end
71
+
72
+ end
73
+
74
+ {:actor => _actor, :act_objects => act_objects, :act_object_ids => act_object_ids,
75
+ :act_targets => act_targets, :act_target_ids => act_target_ids}
76
+ end
77
+
78
+ # Defines a new QueuedTask2 type and registers a definition
79
+ #
80
+ # @param [ String ] name The name of the queued_task
81
+ #
82
+ # @example Define a new queued_task
83
+ # queued_task(:enquiry) do
84
+ # actor :user, :cache => [:full_name]
85
+ # act_object :enquiry, :cache => [:subject]
86
+ # act_target :listing, :cache => [:title]
87
+ # end
88
+ #
89
+ # @return [Definition] Returns the registered definition
90
+ def queued_task(name, &block)
91
+ definition = MergingQueue::DefinitionDSL.new(name)
92
+ definition.instance_eval(&block)
93
+ MergingQueue::Definition.register(definition)
94
+ end
95
+
96
+ # Publishes an queued_task using an queued_task name and data
97
+ #
98
+ # @param [ String ] verb The verb of the queued_task
99
+ # @param [ Hash ] data The data to initialize the queued_task with.
100
+ #
101
+ # @return [MergingQueue::QueuedTask2] An QueuedTask instance with data
102
+ def publish(verb, cur_publish_on, data)
103
+ new.publish({:verb => verb, :publish_on => cur_publish_on}.merge(data))
104
+ end
105
+
106
+ end
107
+
108
+
109
+
110
+ # Publishes the queued_task to the receivers
111
+ #
112
+ # @param [ Hash ] options The options to publish with.
113
+ #
114
+ def publish(data = {})
115
+ assign_properties(data)
116
+
117
+ self
118
+ end
119
+
120
+
121
+ def refresh_data
122
+ save(:validate => false)
123
+ end
124
+
125
+ protected
126
+
127
+ def assign_properties(data = {})
128
+
129
+ self.verb = data.delete(:verb)
130
+
131
+ write_attribute(:publish_on, data[:publish_on])
132
+ data.delete(:publish_on)
133
+
134
+ self.state = 'initial'
135
+
136
+ [:actor, :act_object, :act_target].each do |type|
137
+
138
+ cur_object = data[type]
139
+
140
+ unless cur_object
141
+ if definition.send(type.to_sym)
142
+ raise verb.to_json
143
+ #raise MergingQueue::InvalidData.new(type)
144
+ else
145
+ next
146
+
147
+ end
148
+ end
149
+
150
+ class_sym = cur_object.class.name.to_sym
151
+
152
+ raise MergingQueue::InvalidData.new(class_sym) unless definition.send(type) == class_sym
153
+
154
+ case type
155
+ when :actor
156
+ self.actor = cur_object
157
+ when :act_object
158
+ self.act_object = cur_object
159
+ when :act_target
160
+ self.act_target = cur_object
161
+ else
162
+ raise "unknown type"
163
+ end
164
+
165
+ data.delete(type)
166
+
167
+ end
168
+
169
+ [:grouped_actor].each do |group|
170
+
171
+
172
+ grp_object = data[group]
173
+
174
+ if grp_object == nil
175
+ if definition.send(group.to_sym)
176
+ raise verb.to_json
177
+ #raise MergingQueue::InvalidData.new(group)
178
+ else
179
+ next
180
+
181
+ end
182
+ end
183
+
184
+ grp_object.each do |cur_obj|
185
+ raise MergingQueue::InvalidData.new(class_sym) unless definition.send(group) == cur_obj.class.name.to_sym
186
+
187
+ self.grouped_actors << cur_obj
188
+
189
+ end
190
+
191
+ data.delete(group)
192
+
193
+ end
194
+
195
+ cur_bond_type = definition.send(:bond_type)
196
+
197
+ if cur_bond_type
198
+ write_attribute( :bond_type, cur_bond_type.to_s )
199
+ end
200
+
201
+ def_options = definition.send(:options)
202
+ def_options.each do |cur_option|
203
+ cur_object = data[cur_option]
204
+
205
+ if cur_object
206
+
207
+ if cur_option == :description
208
+ self.description = cur_object
209
+ else
210
+ options[cur_option] = cur_object
211
+ end
212
+ data.delete(cur_option)
213
+
214
+ else
215
+ #all options defined must be used
216
+ raise Streama::InvalidData.new(cur_object[0])
217
+ end
218
+ end
219
+
220
+ if data.size > 0
221
+ raise "unexpected arguments: " + data.to_json
222
+ end
223
+
224
+
225
+
226
+ self.save
227
+
228
+
229
+ end
230
+
231
+ def definition
232
+ @definition ||= MergingQueue::Definition.find(verb)
233
+ end
234
+
235
+
236
+ end
237
+ end
@@ -0,0 +1,3 @@
1
+ module MergingQueue
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,29 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "merging-queue/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "merging-queue"
7
+ s.version = MergingQueue::VERSION
8
+ s.authors = ["Andreas Saebjoernsen"]
9
+ s.email = ["andreas.saebjoernsen@gmail.com"]
10
+ s.homepage = ""
11
+ s.summary = %q{QueuedTask Streams for rails}
12
+ s.description = %q{MergingQueue is a simple gem for grouping tasks by type and time using the ActiveRecord ODM framework and background jobs}
13
+ s.homepage = 'https://github.com/digitalplaywright/merging-queue'
14
+
15
+ s.rubyforge_project = "merging-queue"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+
22
+ s.required_ruby_version = '>= 1.9.3'
23
+
24
+ s.add_dependency 'activerecord', '>= 3'
25
+ s.add_dependency 'activesupport', '>= 3'
26
+ s.add_dependency 'actionpack', '>= 3'
27
+ s.add_development_dependency 'rake'
28
+
29
+ end
@@ -0,0 +1,30 @@
1
+ # Migration responsible for creating a table with queued_tasks
2
+ class CreateQueuedTasks < ActiveRecord::Migration
3
+ # Create table
4
+ def self.up
5
+ create_table :queued_tasks do |t|
6
+ t.belongs_to :actor, :polymorphic => true
7
+ t.belongs_to :act_object, :polymorphic => true
8
+ t.belongs_to :act_target, :polymorphic => true
9
+
10
+ t.string :verb
11
+ t.string :description
12
+ t.string :options
13
+ t.string :bond_type
14
+
15
+ t.time :publish_on
16
+ t.string :state
17
+
18
+ t.timestamps
19
+ end
20
+
21
+ add_index :queued_tasks, [:verb]
22
+ add_index :queued_tasks, [:actor_id, :actor_type]
23
+ add_index :queued_tasks, [:act_object_id, :act_object_type]
24
+ add_index :queued_tasks, [:act_target_id, :act_target_type]
25
+ end
26
+ # Drop table
27
+ def self.down
28
+ drop_table :queued_tasks
29
+ end
30
+ end
@@ -0,0 +1,11 @@
1
+ class CreateArticles < ActiveRecord::Migration
2
+ def self.up
3
+ puts "creating"
4
+ create_table :articles do |t|
5
+ t.string :name
6
+ t.boolean :published
7
+ t.belongs_to :user
8
+ t.timestamps
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,8 @@
1
+ class CreateUsers < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :users do |t|
4
+ t.string :name
5
+ t.timestamps
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,7 @@
1
+ class AddNonstandardToActivities < ActiveRecord::Migration
2
+ def change
3
+ change_table :queued_tasks do |t|
4
+ t.string :nonstandard
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,9 @@
1
+ class CreateVolumes < ActiveRecord::Migration
2
+ def self.up
3
+ puts "creating"
4
+ create_table :volumes do |t|
5
+ t.belongs_to :user
6
+ t.timestamps
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,73 @@
1
+ require "rubygems"
2
+ require "bundler"
3
+ Bundler.setup(:default, :test)
4
+
5
+ $:.unshift File.expand_path('../../lib/', __FILE__)
6
+ require 'active_support/testing/setup_and_teardown'
7
+ require 'merging-queue'
8
+ require 'minitest/autorun'
9
+
10
+ #LiveStream.config # touch config to load ORM, needed in some separate tests
11
+
12
+ require 'active_record'
13
+ require 'active_record/connection_adapters/sqlite3_adapter'
14
+
15
+
16
+ class ActiveSupport::TestCase
17
+ # Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in alphabetical order.
18
+ #
19
+ # Note: You'll currently still have to declare fixtures explicitly in integration tests
20
+ # -- they do not yet inherit this setting
21
+ #fixtures :all
22
+
23
+ # Add more helper methods to be used by all tests here...
24
+ end
25
+
26
+ class QueuedTask < ActiveRecord::Base
27
+ include MergingQueue::QueuedTask
28
+
29
+ queued_task :new_enquiry do
30
+ actor :User
31
+ act_object :Article
32
+ act_target :Volume
33
+ #option :description
34
+ end
35
+
36
+ queued_task :test_description do
37
+ actor :User
38
+ act_object :Article
39
+ act_target :Volume
40
+ option :description
41
+ end
42
+
43
+ queued_task :test_option do
44
+ actor :User
45
+ act_object :Article
46
+ act_target :Volume
47
+ option :country
48
+ end
49
+
50
+ queued_task :test_bond_type do
51
+ actor :User
52
+ act_object :Article
53
+ act_target :Volume
54
+ bond_type :global
55
+ end
56
+
57
+ end
58
+
59
+ class User < ActiveRecord::Base
60
+ include MergingQueue::Actor
61
+
62
+ end
63
+
64
+ class Article < ActiveRecord::Base
65
+
66
+ end
67
+
68
+ class Volume < ActiveRecord::Base
69
+
70
+ end
71
+
72
+ ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ':memory:')
73
+ ActiveRecord::Migrator.migrate(File.expand_path('../migrations', __FILE__))
@@ -0,0 +1,92 @@
1
+ class QueuedTaskTest < ActiveSupport::TestCase
2
+
3
+ def test_truth
4
+ assert true
5
+ end
6
+
7
+ def test_register_definition
8
+
9
+ @definition = QueuedTask.queued_task(:test_queued_task) do
10
+ actor :user, :cache => [:full_name]
11
+ act_object :listing, :cache => [:title, :full_address]
12
+ act_target :listing, :cache => [:title]
13
+ end
14
+
15
+ assert @definition.is_a?(MergingQueue::Definition)
16
+
17
+ end
18
+
19
+ def test_publish_new_queued_task
20
+ _user = User.create()
21
+ _article = Article.create()
22
+ _volume = Volume.create()
23
+
24
+ _queued_task = QueuedTask.publish(:new_enquiry, Time.now, :actor => _user, :act_object => _article, :act_target => _volume)
25
+
26
+ assert _queued_task.persisted?
27
+ #_queued_task.should be_an_instance_of QueuedTask
28
+
29
+ end
30
+
31
+ def test_description
32
+ _user = User.create()
33
+ _article = Article.create()
34
+ _volume = Volume.create()
35
+
36
+ _description = "this is a test"
37
+ _queued_task = QueuedTask.publish(:test_description, Time.now, :actor => _user, :act_object => _article, :act_target => _volume,
38
+ :description => _description )
39
+
40
+ assert _queued_task.description == _description
41
+
42
+ end
43
+
44
+ def test_options
45
+ _user = User.create()
46
+ _article = Article.create()
47
+ _volume = Volume.create()
48
+
49
+ _country = "denmark"
50
+ _queued_task = QueuedTask.publish(:test_option, Time.now, :actor => _user, :act_object => _article, :act_target => _volume,
51
+ :country => _country )
52
+
53
+ assert _queued_task.options[:country] == _country
54
+
55
+ end
56
+
57
+ def test_bond_type
58
+ _user = User.create()
59
+ _article = Article.create()
60
+ _volume = Volume.create()
61
+
62
+ _queued_task = QueuedTask.publish(:test_bond_type, Time.now, :actor => _user, :act_object => _article, :act_target => _volume)
63
+
64
+ assert _queued_task.bond_type == 'global'
65
+
66
+ end
67
+
68
+ def test_poll_changes
69
+
70
+ _user = User.create()
71
+ _article = Article.create()
72
+ _volume = Volume.create()
73
+
74
+ QueuedTask.publish(:test_bond_type, Time.now, :actor => _user, :act_object => _article, :act_target => _volume)
75
+
76
+ assert QueuedTask.all.size > 0
77
+
78
+ QueuedTask.poll_for_changes() do |verb, hash|
79
+ #assert hash[:actor].id == _user.id
80
+ end
81
+
82
+ assert QueuedTask.all.size == 0
83
+
84
+
85
+ end
86
+
87
+
88
+
89
+
90
+
91
+
92
+ end
@@ -0,0 +1,46 @@
1
+ class ActorTest < ActiveSupport::TestCase
2
+
3
+
4
+ def test_publish_queued_task
5
+
6
+ _user = User.create()
7
+ _article = Article.create()
8
+ _volume = Volume.create()
9
+
10
+ queued_task = _user.publish_queued_task(:new_enquiry, Time.now, :act_object => _article, :act_target => _volume)
11
+
12
+ assert queued_task.persisted?
13
+
14
+ end
15
+
16
+ def test_retrieves_the_stream_for_an_actor
17
+ _user = User.create()
18
+ _article = Article.create()
19
+ _volume = Volume.create()
20
+
21
+ _user.publish_queued_task(:new_enquiry, Time.now, :act_object => _article, :act_target => _volume)
22
+ _user.publish_queued_task(:new_enquiry, Time.now, :act_object => _article, :act_target => _volume)
23
+
24
+ assert _user.queued_tasks.size == 2
25
+
26
+ end
27
+
28
+
29
+ def test_retrieves_the_stream_for_a_particular_queued_task_type
30
+ _user = User.create()
31
+ _article = Article.create()
32
+ _volume = Volume.create()
33
+
34
+ _user.publish_queued_task(:new_enquiry, Time.now, :act_object => _article, :act_target => _volume)
35
+ _user.publish_queued_task(:new_enquiry, Time.now, :act_object => _article, :act_target => _volume)
36
+ _user.publish_queued_task(:test_bond_type, Time.now, :act_object => _article, :act_target => _volume)
37
+
38
+ assert _user.queued_merging_tasks(:verb => 'new_enquiry').size == 2
39
+ assert _user.queued_merging_tasks(:bond_type => 'global').size == 1
40
+
41
+ end
42
+
43
+
44
+
45
+
46
+ end
@@ -0,0 +1,29 @@
1
+ class DefinitionDslTest < ActiveSupport::TestCase
2
+
3
+ def definition_dsl
4
+ MergingQueue::DefinitionDSL.new(:new_enquiry)
5
+ end
6
+
7
+ def test_initializes_with_name
8
+ assert definition_dsl.attributes[:name] == :new_enquiry
9
+ end
10
+
11
+ def test_adds_an_actor_to_the_definition
12
+ dsl = definition_dsl
13
+ dsl.actor(:User)
14
+ assert dsl.attributes[:actor] == :User
15
+ end
16
+
17
+ def test_adds_an_act_object_to_the_definition
18
+ dsl = definition_dsl
19
+ dsl.act_object(:Article)
20
+ assert dsl.attributes[:act_object] == :Article
21
+ end
22
+
23
+ def test_adds_a_act_target_to_the_definition
24
+ dsl = definition_dsl
25
+ dsl.act_target(:Volume)
26
+ assert dsl.attributes[:act_target] == :Volume
27
+ end
28
+
29
+ end
@@ -0,0 +1,51 @@
1
+ class DefinitionTest < ActiveSupport::TestCase
2
+
3
+ def definition_dsl
4
+ dsl = MergingQueue::DefinitionDSL.new(:new_enquiry)
5
+ dsl.actor(:User)
6
+ dsl.act_object(:Article)
7
+ dsl.act_target(:Volume)
8
+ dsl
9
+ end
10
+
11
+ def test_initialization
12
+ _definition_dsl = definition_dsl
13
+ _definition = MergingQueue::Definition.new(_definition_dsl)
14
+
15
+ assert _definition.actor == :User
16
+ assert _definition.act_object == :Article
17
+ assert _definition.act_target == :Volume
18
+
19
+ end
20
+
21
+ def test_register_definition_and_return_new_definition
22
+
23
+ assert MergingQueue::Definition.register(definition_dsl).is_a?(MergingQueue::Definition)
24
+
25
+ end
26
+
27
+ def test_register_invalid_definition
28
+
29
+ assert MergingQueue::Definition.register(false) == false
30
+
31
+ end
32
+
33
+ def test_return_registered_definitions
34
+
35
+ MergingQueue::Definition.register(definition_dsl)
36
+ assert MergingQueue::Definition.registered.size > 0
37
+
38
+ end
39
+
40
+ def test_return_definition_by_name
41
+ assert MergingQueue::Definition.find(:new_enquiry).name == :new_enquiry
42
+
43
+ end
44
+
45
+ def test_raise_exception_if_invalid_queued_task
46
+
47
+ assert_raises(MergingQueue::InvalidQueuedTask){ MergingQueue::Definition.find(:unknown_queued_task) }
48
+
49
+ end
50
+
51
+ end
metadata ADDED
@@ -0,0 +1,142 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: merging-queue
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Andreas Saebjoernsen
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-07-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activerecord
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '3'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '3'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: actionpack
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '3'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '3'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: MergingQueue is a simple gem for grouping tasks by type and time using
70
+ the ActiveRecord ODM framework and background jobs
71
+ email:
72
+ - andreas.saebjoernsen@gmail.com
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - .document
78
+ - .gitignore
79
+ - .rspec
80
+ - .travis.yml
81
+ - Gemfile
82
+ - LICENSE.txt
83
+ - README.md
84
+ - Rakefile
85
+ - lib/generators/merging-queue.rb
86
+ - lib/generators/merging-queue/activity/activity_generator.rb
87
+ - lib/generators/merging-queue/activity/templates/activity.rb
88
+ - lib/generators/merging-queue/migration/migration_generator.rb
89
+ - lib/generators/merging-queue/migration/templates/migration.rb
90
+ - lib/merging-queue.rb
91
+ - lib/merging-queue/actor.rb
92
+ - lib/merging-queue/definition.rb
93
+ - lib/merging-queue/definition_dsl.rb
94
+ - lib/merging-queue/errors.rb
95
+ - lib/merging-queue/queued_task.rb
96
+ - lib/merging-queue/version.rb
97
+ - merging-queue.gemspec
98
+ - test/migrations/001_create_queued_tasks.rb
99
+ - test/migrations/002_create_articles.rb
100
+ - test/migrations/003_create_users.rb
101
+ - test/migrations/004_add_nonstandard_to_activities.rb
102
+ - test/migrations/005_create_volumes.rb
103
+ - test/test_helper.rb
104
+ - test/unit/activity_test.rb
105
+ - test/unit/actor_test.rb
106
+ - test/unit/definition_dsl_test.rb
107
+ - test/unit/definition_test.rb
108
+ homepage: https://github.com/digitalplaywright/merging-queue
109
+ licenses: []
110
+ metadata: {}
111
+ post_install_message:
112
+ rdoc_options: []
113
+ require_paths:
114
+ - lib
115
+ required_ruby_version: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - '>='
118
+ - !ruby/object:Gem::Version
119
+ version: 1.9.3
120
+ required_rubygems_version: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - '>='
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ requirements: []
126
+ rubyforge_project: merging-queue
127
+ rubygems_version: 2.0.4
128
+ signing_key:
129
+ specification_version: 4
130
+ summary: QueuedTask Streams for rails
131
+ test_files:
132
+ - test/migrations/001_create_queued_tasks.rb
133
+ - test/migrations/002_create_articles.rb
134
+ - test/migrations/003_create_users.rb
135
+ - test/migrations/004_add_nonstandard_to_activities.rb
136
+ - test/migrations/005_create_volumes.rb
137
+ - test/test_helper.rb
138
+ - test/unit/activity_test.rb
139
+ - test/unit/actor_test.rb
140
+ - test/unit/definition_dsl_test.rb
141
+ - test/unit/definition_test.rb
142
+ has_rdoc: