triggerable 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.
Files changed (39) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +3 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +89 -0
  8. data/Rakefile +6 -0
  9. data/lib/triggerable.rb +78 -0
  10. data/lib/triggerable/actions.rb +27 -0
  11. data/lib/triggerable/conditions/condition.rb +42 -0
  12. data/lib/triggerable/conditions/field/exists.rb +12 -0
  13. data/lib/triggerable/conditions/field/field_condition.rb +29 -0
  14. data/lib/triggerable/conditions/field/in.rb +17 -0
  15. data/lib/triggerable/conditions/field/or_equal_to.rb +15 -0
  16. data/lib/triggerable/conditions/lambda_condition.rb +12 -0
  17. data/lib/triggerable/conditions/method_condition.rb +11 -0
  18. data/lib/triggerable/conditions/predicate/and.rb +7 -0
  19. data/lib/triggerable/conditions/predicate/or.rb +7 -0
  20. data/lib/triggerable/conditions/predicate/predicate_condition.rb +46 -0
  21. data/lib/triggerable/conditions/schedule/after.rb +28 -0
  22. data/lib/triggerable/conditions/schedule/before.rb +35 -0
  23. data/lib/triggerable/conditions/schedule/schedule_condition.rb +33 -0
  24. data/lib/triggerable/engine.rb +28 -0
  25. data/lib/triggerable/rules/automation.rb +13 -0
  26. data/lib/triggerable/rules/rule.rb +11 -0
  27. data/lib/triggerable/rules/trigger.rb +14 -0
  28. data/lib/triggerable/version.rb +3 -0
  29. data/spec/conditions_spec.rb +200 -0
  30. data/spec/integration/actions_spec.rb +37 -0
  31. data/spec/integration/automations_spec.rb +353 -0
  32. data/spec/integration/conditions_spec.rb +144 -0
  33. data/spec/integration/short_syntax_spec.rb +92 -0
  34. data/spec/models.rb +23 -0
  35. data/spec/schema.rb +27 -0
  36. data/spec/scopes_spec.rb +78 -0
  37. data/spec/spec_helper.rb +22 -0
  38. data/triggerable.gemspec +28 -0
  39. metadata +191 -0
@@ -0,0 +1,92 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Short syntax' do
4
+ before(:each) do
5
+ Engine.clear
6
+ TestTask.destroy_all
7
+ end
8
+
9
+ it 'is' do
10
+ TestTask.trigger on: :after_update, if: {status: 'solved'} do
11
+ TestTask.create kind: 'follow up'
12
+ end
13
+
14
+ task = TestTask.create
15
+ expect(TestTask.count).to eq(1)
16
+
17
+ task.update_attributes status: 'solved'
18
+ expect(TestTask.count).to eq(2)
19
+ expect(TestTask.all.last.kind).to eq('follow up')
20
+ end
21
+
22
+ it 'in' do
23
+ TestTask.trigger on: :after_update, if: {status: ['solved', 'confirmed']} do
24
+ TestTask.create kind: 'follow up'
25
+ end
26
+
27
+ task = TestTask.create
28
+ expect(TestTask.count).to eq(1)
29
+
30
+ task.update_attributes status: 'solved'
31
+ expect(TestTask.count).to eq(2)
32
+ expect(TestTask.all.last.kind).to eq('follow up')
33
+
34
+ task2 = TestTask.create
35
+ expect(TestTask.count).to eq(3)
36
+
37
+ task2.update_attributes status: 'confirmed'
38
+ expect(TestTask.count).to eq(4)
39
+ expect(TestTask.all.last.kind).to eq('follow up')
40
+ end
41
+
42
+ it 'method call' do
43
+ TestTask.trigger on: :after_update, if: :solved? do
44
+ TestTask.create kind: 'follow up'
45
+ end
46
+
47
+ task = TestTask.create
48
+ expect(TestTask.count).to eq(1)
49
+
50
+ task.update_attributes status: 'solved'
51
+ expect(TestTask.count).to eq(2)
52
+ expect(TestTask.all.last.kind).to eq('follow up')
53
+ end
54
+
55
+ it 'method predicate call' do
56
+ TestTask.trigger on: :after_update, if: { and: [:solved?, :true_method] } do
57
+ TestTask.create kind: 'follow up'
58
+ end
59
+
60
+ task = TestTask.create
61
+ expect(TestTask.count).to eq(1)
62
+
63
+ task.update_attributes status: 'solved'
64
+ expect(TestTask.count).to eq(2)
65
+ expect(TestTask.all.last.kind).to eq('follow up')
66
+ end
67
+
68
+ it 'method predicate call 2' do
69
+ TestTask.trigger on: :after_update, if: { or: [:solved?, :true_method] } do
70
+ TestTask.create kind: 'follow up'
71
+ end
72
+
73
+ task = TestTask.create
74
+ expect(TestTask.count).to eq(1)
75
+
76
+ task.update_attributes status: 'not_solved'
77
+ expect(TestTask.count).to eq(2)
78
+ expect(TestTask.all.last.kind).to eq('follow up')
79
+ end
80
+
81
+ it 'passes context to action' do
82
+ TestTask.trigger on: :before_update, if: :solved? do
83
+ self.status = 'confirmed'
84
+ end
85
+
86
+ task = TestTask.create
87
+ expect(TestTask.count).to eq(1)
88
+
89
+ task.update_attributes status: 'solved'
90
+ expect(task.status).to eq('confirmed')
91
+ end
92
+ end
data/spec/models.rb ADDED
@@ -0,0 +1,23 @@
1
+ require 'active_record'
2
+
3
+ class TestTask < ActiveRecord::Base
4
+ def solved?
5
+ status == 'solved'
6
+ end
7
+
8
+ def true_method
9
+ true
10
+ end
11
+
12
+ def false_method
13
+ false
14
+ end
15
+ end
16
+
17
+ class ParentModel < ActiveRecord::Base
18
+ has_many :child_models
19
+ end
20
+
21
+ class ChildModel < ActiveRecord::Base
22
+ belongs_to :parent_model
23
+ end
data/spec/schema.rb ADDED
@@ -0,0 +1,27 @@
1
+ ActiveRecord::Schema.define do
2
+ self.verbose = false
3
+
4
+ create_table :test_tasks do |t|
5
+ t.string :kind
6
+ t.string :status
7
+ t.integer :failure_count
8
+ t.datetime :scheduled_at
9
+ t.timestamps
10
+ end
11
+
12
+ create_table :parent_models, force: true do |t|
13
+ t.string :string_field
14
+ t.integer :integer_field
15
+
16
+ t.timestamps
17
+ end
18
+
19
+ create_table :child_models, force: true do |t|
20
+ t.string :string_field
21
+ t.integer :integer_field
22
+ t.integer :parent_model_id
23
+
24
+ t.timestamps
25
+ end
26
+
27
+ end
@@ -0,0 +1,78 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Scopes' do
4
+ before :each do
5
+ ParentModel.destroy_all
6
+
7
+ @m1 = ParentModel.create(integer_field: 1, string_field: 'a')
8
+ @m2 = ParentModel.create(integer_field: 2, string_field: 'b')
9
+ @m3 = ParentModel.create(integer_field: 1, string_field: 'c')
10
+ end
11
+
12
+ context 'field conditions' do
13
+ def model_ids_for condition_class, arg
14
+ table = Arel::Table.new(:parent_models)
15
+ scope = condition_class.new(:integer_field, arg).scope(table)
16
+ query = table.where(scope).project(Arel.sql('id')).to_sql
17
+
18
+ ParentModel.connection.execute(query).map { |r| r['id'] }
19
+ end
20
+
21
+ it 'is' do
22
+ ids = model_ids_for(Conditions::Is, 1)
23
+ expect(ids).to eq([@m1.id, @m3.id])
24
+ end
25
+
26
+ it 'greater than' do
27
+ ids = model_ids_for(Conditions::GreaterThan, 1)
28
+ expect(ids).to eq([@m2.id])
29
+ end
30
+
31
+ it 'less than' do
32
+ ids = model_ids_for(Conditions::LessThan, 2)
33
+ expect(ids).to eq([@m1.id, @m3.id])
34
+ end
35
+
36
+ it 'is not' do
37
+ ids = model_ids_for(Conditions::IsNot, 2)
38
+ expect(ids).to eq([@m1.id, @m3.id])
39
+ end
40
+
41
+ it 'greater than or equal' do
42
+ ids = model_ids_for(Conditions::LessThanOrEqualTo, 2)
43
+ expect(ids).to eq([@m1.id, @m2.id, @m3.id])
44
+ end
45
+
46
+ it 'less than or equal' do
47
+ ids = model_ids_for(Conditions::GreaterThanOrEqualTo, 1)
48
+ expect(ids).to eq([@m1.id, @m2.id, @m3.id])
49
+ end
50
+
51
+ it 'in' do
52
+ ids = model_ids_for(Conditions::In, [1, 2])
53
+ expect(ids).to eq([@m1.id, @m2.id, @m3.id])
54
+ end
55
+ end
56
+
57
+ context 'predicates' do
58
+ def model_ids_for condition_class, conditions
59
+ and_condition = condition_class.new([])
60
+ and_condition.conditions = [Conditions::Is.new(:integer_field, 1), Conditions::Is.new(:string_field, 'c')]
61
+
62
+ table = Arel::Table.new(:parent_models)
63
+ query = table.where(and_condition.scope(table)).project(Arel.sql('id')).to_sql
64
+
65
+ ParentModel.connection.execute(query).map { |r| r['id'] }
66
+ end
67
+
68
+ it 'and' do
69
+ ids = model_ids_for Conditions::And, [Conditions::Is.new(:integer_field, 1), Conditions::Is.new(:string_field, 'c')]
70
+ expect(ids).to eq([@m3.id])
71
+ end
72
+
73
+ it 'or' do
74
+ ids = model_ids_for Conditions::Or, [Conditions::Is.new(:integer_field, 1), Conditions::Is.new(:string_field, 'c')]
75
+ expect(ids).to eq([@m1.id, @m3.id])
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,22 @@
1
+ require 'simplecov'
2
+ SimpleCov.start
3
+
4
+ require 'active_record'
5
+
6
+ ActiveRecord::Base.establish_connection adapter: "sqlite3", database: ":memory:"
7
+
8
+ require 'triggerable'
9
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
10
+
11
+ load 'schema.rb'
12
+ require 'models'
13
+
14
+ RSpec.configure do |config|
15
+ config.mock_with :rspec do |mocks|
16
+ mocks.syntax = :should
17
+ end
18
+ end
19
+
20
+ def constantize_time_now(time)
21
+ Time.stub(:now).and_return(time)
22
+ end
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'triggerable/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "triggerable"
8
+ spec.version = Triggerable::VERSION
9
+ spec.authors = ["DmitryTsepelev"]
10
+ spec.email = ["dmitry.a.tsepelev@gmail.com"]
11
+ spec.description = "Triggers/automations engine"
12
+ spec.summary = "Triggerable is a powerful engine for adding a conditional behaviour for ActiveRecord models."
13
+ spec.homepage = "https://github.com/anjlab/triggerable"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rspec"
24
+ spec.add_development_dependency "activesupport", "~> 4.0.0"
25
+ spec.add_development_dependency "activerecord", "~> 4.0.0"
26
+ spec.add_development_dependency "sqlite3"
27
+ spec.add_development_dependency "simplecov"
28
+ end
metadata ADDED
@@ -0,0 +1,191 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: triggerable
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - DmitryTsepelev
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-10-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: activesupport
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 4.0.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 4.0.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: activerecord
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 4.0.0
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 4.0.0
83
+ - !ruby/object:Gem::Dependency
84
+ name: sqlite3
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: simplecov
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ description: Triggers/automations engine
112
+ email:
113
+ - dmitry.a.tsepelev@gmail.com
114
+ executables: []
115
+ extensions: []
116
+ extra_rdoc_files: []
117
+ files:
118
+ - ".gitignore"
119
+ - ".rspec"
120
+ - ".travis.yml"
121
+ - Gemfile
122
+ - LICENSE.txt
123
+ - README.md
124
+ - Rakefile
125
+ - lib/.DS_Store
126
+ - lib/triggerable.rb
127
+ - lib/triggerable/actions.rb
128
+ - lib/triggerable/conditions/condition.rb
129
+ - lib/triggerable/conditions/field/exists.rb
130
+ - lib/triggerable/conditions/field/field_condition.rb
131
+ - lib/triggerable/conditions/field/in.rb
132
+ - lib/triggerable/conditions/field/or_equal_to.rb
133
+ - lib/triggerable/conditions/lambda_condition.rb
134
+ - lib/triggerable/conditions/method_condition.rb
135
+ - lib/triggerable/conditions/predicate/and.rb
136
+ - lib/triggerable/conditions/predicate/or.rb
137
+ - lib/triggerable/conditions/predicate/predicate_condition.rb
138
+ - lib/triggerable/conditions/schedule/after.rb
139
+ - lib/triggerable/conditions/schedule/before.rb
140
+ - lib/triggerable/conditions/schedule/schedule_condition.rb
141
+ - lib/triggerable/engine.rb
142
+ - lib/triggerable/rules/.DS_Store
143
+ - lib/triggerable/rules/automation.rb
144
+ - lib/triggerable/rules/rule.rb
145
+ - lib/triggerable/rules/trigger.rb
146
+ - lib/triggerable/version.rb
147
+ - spec/conditions_spec.rb
148
+ - spec/integration/actions_spec.rb
149
+ - spec/integration/automations_spec.rb
150
+ - spec/integration/conditions_spec.rb
151
+ - spec/integration/short_syntax_spec.rb
152
+ - spec/models.rb
153
+ - spec/schema.rb
154
+ - spec/scopes_spec.rb
155
+ - spec/spec_helper.rb
156
+ - triggerable.gemspec
157
+ homepage: https://github.com/anjlab/triggerable
158
+ licenses:
159
+ - MIT
160
+ metadata: {}
161
+ post_install_message:
162
+ rdoc_options: []
163
+ require_paths:
164
+ - lib
165
+ required_ruby_version: !ruby/object:Gem::Requirement
166
+ requirements:
167
+ - - ">="
168
+ - !ruby/object:Gem::Version
169
+ version: '0'
170
+ required_rubygems_version: !ruby/object:Gem::Requirement
171
+ requirements:
172
+ - - ">="
173
+ - !ruby/object:Gem::Version
174
+ version: '0'
175
+ requirements: []
176
+ rubyforge_project:
177
+ rubygems_version: 2.2.2
178
+ signing_key:
179
+ specification_version: 4
180
+ summary: Triggerable is a powerful engine for adding a conditional behaviour for ActiveRecord
181
+ models.
182
+ test_files:
183
+ - spec/conditions_spec.rb
184
+ - spec/integration/actions_spec.rb
185
+ - spec/integration/automations_spec.rb
186
+ - spec/integration/conditions_spec.rb
187
+ - spec/integration/short_syntax_spec.rb
188
+ - spec/models.rb
189
+ - spec/schema.rb
190
+ - spec/scopes_spec.rb
191
+ - spec/spec_helper.rb