merging-queue 0.1.0

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