stance 0.3.0 → 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 51b0695fffdd5e9543b325277fabfd2244cac3a84ffefb2660ed9c48499bdf12
4
- data.tar.gz: 28c77b84783964cc08617e2d28b8c1906105fe621b4ad83236bb571327e6dfc0
3
+ metadata.gz: '0094c269ae824b6cf2ecf9c2684dc3ef421b0517eb200771811e50e22d2cb329'
4
+ data.tar.gz: b49537957c9cc39e52e779d0205ad05057355a513327a852cdd9de68fc3b472e
5
5
  SHA512:
6
- metadata.gz: 2816cde14c5643554d55faa81122667e13b123bc8591e8d959d412a40e204b72aeca434b41a4b98a5270b07f828652f59064e60851c5e9e83a34aa21b6c97b52
7
- data.tar.gz: '049bd51dd0ddb46eaa7e52b4a2fa0f7a709fd1c101510499637411c5d55e0ae374e5fca1de23a30760276213b1b38b43a56c353f392df9fe20272cee46eb0dcc'
6
+ metadata.gz: c4031fcd0b7366810a4787570d8e351957bbdf7f720a3df10a8c9d7d477e78911fbec1e5e9375ee8bd61e612bdf60dc98a955f661a0621cef4c9c05f4282d219
7
+ data.tar.gz: 22fb2da87d3c7b90a022ba34f33104fbf4cf47931e4f14268f0e50b50181e07344a370ed5a7798b312792858f0f40bc287fffb2996c8fa8bff721d4651a25c0c
data/.rubocop.yml CHANGED
@@ -5,4 +5,6 @@ Style/Documentation:
5
5
  Layout/LineLength:
6
6
  Max: 100
7
7
  Metrics/MethodLength:
8
- Max: 12
8
+ Max: 15
9
+ Style/ClassVars:
10
+ Enabled: false
data/Gemfile CHANGED
@@ -10,6 +10,9 @@ gem 'combustion'
10
10
  gem 'minitest'
11
11
  gem 'minitest-autotest'
12
12
  gem 'minitest-focus'
13
- gem 'rake', '~> 12.0'
13
+ gem 'mocha'
14
+ gem 'rake', '~> 13.0'
14
15
  gem 'rubocop'
16
+ gem 'rubocop-minitest'
17
+ gem 'rubocop-rake'
15
18
  gem 'sqlite3'
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- stance (0.2.0)
4
+ stance (0.5.1)
5
5
  activerecord (>= 5)
6
6
  multi_json
7
7
  railties (>= 5)
@@ -9,62 +9,65 @@ PATH
9
9
  GEM
10
10
  remote: https://rubygems.org/
11
11
  specs:
12
- actionpack (6.0.3.2)
13
- actionview (= 6.0.3.2)
14
- activesupport (= 6.0.3.2)
15
- rack (~> 2.0, >= 2.0.8)
12
+ actionpack (6.1.4)
13
+ actionview (= 6.1.4)
14
+ activesupport (= 6.1.4)
15
+ rack (~> 2.0, >= 2.0.9)
16
16
  rack-test (>= 0.6.3)
17
17
  rails-dom-testing (~> 2.0)
18
18
  rails-html-sanitizer (~> 1.0, >= 1.2.0)
19
- actionview (6.0.3.2)
20
- activesupport (= 6.0.3.2)
19
+ actionview (6.1.4)
20
+ activesupport (= 6.1.4)
21
21
  builder (~> 3.1)
22
22
  erubi (~> 1.4)
23
23
  rails-dom-testing (~> 2.0)
24
24
  rails-html-sanitizer (~> 1.1, >= 1.2.0)
25
- activemodel (6.0.3.2)
26
- activesupport (= 6.0.3.2)
27
- activerecord (6.0.3.2)
28
- activemodel (= 6.0.3.2)
29
- activesupport (= 6.0.3.2)
30
- activesupport (6.0.3.2)
25
+ activemodel (6.1.4)
26
+ activesupport (= 6.1.4)
27
+ activerecord (6.1.4)
28
+ activemodel (= 6.1.4)
29
+ activesupport (= 6.1.4)
30
+ activesupport (6.1.4)
31
31
  concurrent-ruby (~> 1.0, >= 1.0.2)
32
- i18n (>= 0.7, < 2)
33
- minitest (~> 5.1)
34
- tzinfo (~> 1.1)
35
- zeitwerk (~> 2.2, >= 2.2.2)
36
- ast (2.4.1)
32
+ i18n (>= 1.6, < 2)
33
+ minitest (>= 5.1)
34
+ tzinfo (~> 2.0)
35
+ zeitwerk (~> 2.3)
36
+ ast (2.4.2)
37
37
  autotest-suffix (1.1.0)
38
38
  builder (3.2.4)
39
- combustion (1.3.0)
39
+ combustion (1.3.1)
40
40
  activesupport (>= 3.0.0)
41
41
  railties (>= 3.0.0)
42
42
  thor (>= 0.14.6)
43
- concurrent-ruby (1.1.6)
43
+ concurrent-ruby (1.1.9)
44
44
  crass (1.0.6)
45
- erubi (1.9.0)
46
- i18n (1.8.5)
45
+ erubi (1.10.0)
46
+ i18n (1.8.10)
47
47
  concurrent-ruby (~> 1.0)
48
- loofah (2.6.0)
48
+ loofah (2.10.0)
49
49
  crass (~> 1.0.2)
50
50
  nokogiri (>= 1.5.9)
51
51
  method_source (1.0.0)
52
- mini_portile2 (2.4.0)
53
- minitest (5.14.1)
52
+ mini_portile2 (2.5.3)
53
+ minitest (5.14.4)
54
54
  minitest-autotest (1.1.1)
55
55
  minitest-server (~> 1.0)
56
56
  path_expander (~> 1.0)
57
- minitest-focus (1.2.1)
57
+ minitest-focus (1.3.1)
58
58
  minitest (>= 4, < 6)
59
59
  minitest-server (1.0.6)
60
60
  minitest (~> 5.0)
61
+ mocha (1.13.0)
61
62
  multi_json (1.15.0)
62
- nokogiri (1.10.10)
63
- mini_portile2 (~> 2.4.0)
64
- parallel (1.19.2)
65
- parser (2.7.1.4)
63
+ nokogiri (1.11.7)
64
+ mini_portile2 (~> 2.5.0)
65
+ racc (~> 1.4)
66
+ parallel (1.20.1)
67
+ parser (3.0.2.0)
66
68
  ast (~> 2.4.1)
67
69
  path_expander (1.1.0)
70
+ racc (1.5.2)
68
71
  rack (2.2.3)
69
72
  rack-test (1.1.0)
70
73
  rack (>= 1.0, < 3)
@@ -73,35 +76,38 @@ GEM
73
76
  nokogiri (>= 1.6)
74
77
  rails-html-sanitizer (1.3.0)
75
78
  loofah (~> 2.3)
76
- railties (6.0.3.2)
77
- actionpack (= 6.0.3.2)
78
- activesupport (= 6.0.3.2)
79
+ railties (6.1.4)
80
+ actionpack (= 6.1.4)
81
+ activesupport (= 6.1.4)
79
82
  method_source
80
- rake (>= 0.8.7)
81
- thor (>= 0.20.3, < 2.0)
83
+ rake (>= 0.13)
84
+ thor (~> 1.0)
82
85
  rainbow (3.0.0)
83
- rake (12.3.3)
84
- regexp_parser (1.7.1)
85
- rexml (3.2.4)
86
- rubocop (0.89.0)
86
+ rake (13.0.6)
87
+ regexp_parser (2.1.1)
88
+ rexml (3.2.5)
89
+ rubocop (1.18.3)
87
90
  parallel (~> 1.10)
88
- parser (>= 2.7.1.1)
91
+ parser (>= 3.0.0.0)
89
92
  rainbow (>= 2.2.2, < 4.0)
90
- regexp_parser (>= 1.7)
93
+ regexp_parser (>= 1.8, < 3.0)
91
94
  rexml
92
- rubocop-ast (>= 0.1.0, < 1.0)
95
+ rubocop-ast (>= 1.7.0, < 2.0)
93
96
  ruby-progressbar (~> 1.7)
94
- unicode-display_width (>= 1.4.0, < 2.0)
95
- rubocop-ast (0.3.0)
96
- parser (>= 2.7.1.4)
97
- ruby-progressbar (1.10.1)
97
+ unicode-display_width (>= 1.4.0, < 3.0)
98
+ rubocop-ast (1.8.0)
99
+ parser (>= 3.0.1.1)
100
+ rubocop-minitest (0.14.0)
101
+ rubocop (>= 0.90, < 2.0)
102
+ rubocop-rake (0.6.0)
103
+ rubocop (~> 1.0)
104
+ ruby-progressbar (1.11.0)
98
105
  sqlite3 (1.4.2)
99
- thor (1.0.1)
100
- thread_safe (0.3.6)
101
- tzinfo (1.2.7)
102
- thread_safe (~> 0.1)
103
- unicode-display_width (1.7.0)
104
- zeitwerk (2.4.0)
106
+ thor (1.1.0)
107
+ tzinfo (2.0.4)
108
+ concurrent-ruby (~> 1.0)
109
+ unicode-display_width (2.0.0)
110
+ zeitwerk (2.4.2)
105
111
 
106
112
  PLATFORMS
107
113
  ruby
@@ -112,10 +118,13 @@ DEPENDENCIES
112
118
  minitest
113
119
  minitest-autotest
114
120
  minitest-focus
115
- rake (~> 12.0)
121
+ mocha
122
+ rake (~> 13.0)
116
123
  rubocop
124
+ rubocop-minitest
125
+ rubocop-rake
117
126
  sqlite3
118
127
  stance!
119
128
 
120
129
  BUNDLED WITH
121
- 2.1.4
130
+ 2.2.18
data/README.md CHANGED
@@ -1,6 +1,14 @@
1
- # Stance - Simple Events for Rails apps
1
+ # Stance - Simple & Explicit Events for Rails apps
2
+
3
+ ## Usage
2
4
 
3
5
  ```ruby
6
+ # Your model
7
+ class Appointment < ActiveRecord::Base
8
+ include Stance::Eventable
9
+ end
10
+
11
+ # Define your events
4
12
  class AppointmentEvents < Stance::Events
5
13
  # Define events.
6
14
  event :my_event
@@ -13,6 +21,12 @@ class AppointmentEvents < Stance::Events
13
21
  # Singleton event: only one active event with this name can exist for the same subject.
14
22
  event :my_event, singleton: true
15
23
 
24
+ # By default, events are recorded in the database, unless you set the `record` option to false,
25
+ event :my_recordless_event, record: false
26
+
27
+ # You can define a class event, which is published on the class instead of the instance.
28
+ event :my_class_event, class: true
29
+
16
30
  # Will be called before/after each event in this class. Have access to the event `subject` and
17
31
  # `record`.
18
32
  before_create :do_something_before
@@ -30,6 +44,36 @@ end
30
44
  Appointment.find(1).publish_event :some_event
31
45
  Appointment.find(1).publish_event 'offers.create'
32
46
  Appointment.find(1).publish_event :event_with_metadata, foo: :bah
47
+ Appointment.publish_event :my_class_event
48
+ ```
49
+
50
+ ### ActiveRecord Callbacks
51
+
52
+ Stance comes with a couple of opt-in modules to help ease your Callback spaghetti...
53
+
54
+ ```ruby
55
+ class Appointment < ActiveRecord::Base
56
+ include Stance::Eventable
57
+ include Stance::ActiveRecordCallbacks
58
+ end
59
+
60
+ class AppointmentEvents < Stance::Events
61
+ include Stance::ActiveRecordEvents
62
+ end
63
+
64
+ # Now all your model callbacks will trigger an event of the same name, where any public methods
65
+ # defined will be called.
66
+ class AppointmentEvents::AfterCreate < Stance::Event
67
+ include Stance::ActiveRecordEvents
68
+
69
+ # This method will be called upon the :after_create callback of the Appointment model.
70
+ def do_something;end
71
+
72
+ private
73
+
74
+ # Private methods will not be called by the callback.
75
+ def my_private_method;end
76
+ end
33
77
  ```
34
78
 
35
79
  ## Installation
@@ -36,7 +36,7 @@ module Stance
36
36
  end
37
37
 
38
38
  def event_class_name
39
- @event_class_name ||= "#{subject.model_name.name}Events::#{name.tr('.', '/').classify}"
39
+ @event_class_name ||= "#{subject_type}Events::#{name.tr('.', '/').classify}"
40
40
  end
41
41
 
42
42
  def event_class
data/lib/stance.rb CHANGED
@@ -19,4 +19,6 @@ module Stance
19
19
  autoload :Events, 'stance/events'
20
20
  autoload :Event, 'stance/event'
21
21
  autoload :Eventable, 'stance/eventable'
22
+ autoload :ActiveRecordCallbacks, 'stance/active_record_callbacks'
23
+ autoload :ActiveRecordEvents, 'stance/active_record_events'
22
24
  end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Stance
4
+ module ActiveRecordCallbacks
5
+ extend ActiveSupport::Concern
6
+
7
+ CALLBACKS = %i[after_initialize before_validation after_validation before_save before_create
8
+ after_create before_update after_update before_destroy after_destroy after_save
9
+ after_touch after_commit after_save_commit after_create_commit
10
+ after_update_commit after_destroy_commit after_rollback].freeze
11
+
12
+ included do
13
+ CALLBACKS.each do |cb|
14
+ send(cb) { publish_event cb }
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Stance
4
+ module ActiveRecordEvents
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ with_options record: false do
9
+ Stance::ActiveRecordCallbacks::CALLBACKS.each { |ev| event(ev) }
10
+ end
11
+ end
12
+ end
13
+ end
data/lib/stance/event.rb CHANGED
@@ -20,16 +20,32 @@ module Stance
20
20
  end
21
21
 
22
22
  def initialize(name, subject, metadata, options)
23
- @options = { singleton: false }.merge(options)
24
- @record = Stance::EventRecord.new(name: name, subject: subject, metadata: metadata)
23
+ @options = { singleton: false, record: true, class: false }.merge(options)
24
+
25
+ attrs = { name: name, metadata: metadata }
26
+ if subject.is_a?(String)
27
+ attrs[:subject_type] = subject
28
+ else
29
+ attrs[:subject] = subject
30
+ end
31
+ @record = Stance::EventRecord.new(attrs)
25
32
  end
26
33
 
27
34
  def create
28
35
  return self if singleton_exists?
29
36
 
37
+ Rails.logger.info "Event: #{full_name}"
38
+
30
39
  Stance::EventRecord.transaction do
31
40
  run_callbacks :create do
32
- record.save
41
+ # Call each public method of the Event class if a custom class.
42
+ if self.class.name != 'Stance::Event'
43
+ (public_methods(false) - Stance::Event.instance_methods(false)).each do |method|
44
+ send method
45
+ end
46
+ end
47
+
48
+ record.save if @options[:record]
33
49
  end
34
50
  end
35
51
 
@@ -37,7 +53,7 @@ module Stance
37
53
  end
38
54
 
39
55
  def full_name
40
- "#{subject.model_name.singular}.#{name}"
56
+ "#{record.subject_type.downcase}.#{name}"
41
57
  end
42
58
 
43
59
  private
@@ -8,6 +8,43 @@ module Stance
8
8
  has_many :events, as: :subject, class_name: 'Stance::EventRecord'
9
9
  end
10
10
 
11
+ class_methods do
12
+ def publish_event(name, metadata = {})
13
+ name = name.to_s
14
+ ensure_event! name
15
+
16
+ # Find the Event class - if any - and call it. Falls back to Stance::Event.
17
+ ev = event_class(name).new(name, model_name.name, metadata, events_class.events[name])
18
+
19
+ return ev if Stance.disabled_events.include?(ev.full_name)
20
+
21
+ events_class.new(ev).run_callbacks(:create) { ev.create }
22
+ end
23
+
24
+ private
25
+
26
+ # Raise EventNotFound if the class event has not been defined.
27
+ def ensure_event!(name)
28
+ return if events_class.events.one? { |event, options| name == event && options[:class] }
29
+
30
+ raise Stance::EventNotFound, "Class event `#{name}` not found"
31
+ end
32
+
33
+ def events_class
34
+ @events_class ||= events_class_name.constantize
35
+ end
36
+
37
+ def events_class_name
38
+ @events_class_name ||= "#{model_name.name}Events"
39
+ end
40
+
41
+ def event_class(name)
42
+ name.constantize
43
+ rescue NameError
44
+ Stance::Event
45
+ end
46
+ end
47
+
11
48
  # Publish an event.
12
49
  #
13
50
  # Creates an EventRecord with the given `name`, `metadata` and self as the 'subject'.
@@ -18,19 +55,16 @@ module Stance
18
55
  ensure_event! name
19
56
 
20
57
  # Find the Event class - if any - and call it. Falls back to Stance::Event.
21
- event_class_name = "#{events_class_name}::#{name.to_s.tr('.', '/').classify}"
22
- ev = event_class(event_class_name).new(name, self, metadata, events_class.events[name])
58
+ ev = event_class(name).new(name, self, metadata, events_class.events[name])
23
59
 
24
60
  return ev if Stance.disabled_events.include?(ev.full_name)
25
61
 
26
62
  events_class.new(ev).run_callbacks(:create) { ev.create }
27
63
  end
28
64
 
29
- private
30
-
31
65
  # Raise EventNotFound if the event has not been defined.
32
66
  def ensure_event!(name)
33
- return if events_class.events.keys.include?(name)
67
+ return if events_class.events.one? { |event, options| name == event && !options[:class] }
34
68
 
35
69
  raise Stance::EventNotFound, "Event `#{name}` not found"
36
70
  end
@@ -44,7 +78,7 @@ module Stance
44
78
  end
45
79
 
46
80
  def event_class(name)
47
- name.constantize
81
+ "#{events_class_name}::#{name.tr('.', '/').classify}".constantize
48
82
  rescue NameError
49
83
  Stance::Event
50
84
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Stance
4
- VERSION = '0.3.0'
4
+ VERSION = '0.5.2'
5
5
  end
data/stance.gemspec CHANGED
@@ -11,7 +11,7 @@ Gem::Specification.new do |spec|
11
11
  spec.summary = 'Event System for Rails'
12
12
  spec.homepage = 'https://github.com/joelmoss/stance'
13
13
  spec.license = 'MIT'
14
- spec.required_ruby_version = Gem::Requirement.new('>= 2.3.0')
14
+ spec.required_ruby_version = Gem::Requirement.new('>= 2.7.0')
15
15
 
16
16
  spec.metadata['homepage_uri'] = spec.homepage
17
17
  spec.metadata['source_code_uri'] = spec.homepage
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stance
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.5.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joel Moss
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-08-24 00:00:00.000000000 Z
11
+ date: 2021-07-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -52,7 +52,7 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '5'
55
- description:
55
+ description:
56
56
  email:
57
57
  - joel@developwithstyle.com
58
58
  executables: []
@@ -74,6 +74,8 @@ files:
74
74
  - lib/generators/stance/install_generator.rb
75
75
  - lib/generators/stance/templates/migration.rb.tt
76
76
  - lib/stance.rb
77
+ - lib/stance/active_record_callbacks.rb
78
+ - lib/stance/active_record_events.rb
77
79
  - lib/stance/engine.rb
78
80
  - lib/stance/event.rb
79
81
  - lib/stance/eventable.rb
@@ -87,7 +89,7 @@ metadata:
87
89
  homepage_uri: https://github.com/joelmoss/stance
88
90
  source_code_uri: https://github.com/joelmoss/stance
89
91
  changelog_uri: https://github.com/joelmoss/stance/releases
90
- post_install_message:
92
+ post_install_message:
91
93
  rdoc_options: []
92
94
  require_paths:
93
95
  - lib
@@ -95,15 +97,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
95
97
  requirements:
96
98
  - - ">="
97
99
  - !ruby/object:Gem::Version
98
- version: 2.3.0
100
+ version: 2.7.0
99
101
  required_rubygems_version: !ruby/object:Gem::Requirement
100
102
  requirements:
101
103
  - - ">="
102
104
  - !ruby/object:Gem::Version
103
105
  version: '0'
104
106
  requirements: []
105
- rubygems_version: 3.1.2
106
- signing_key:
107
+ rubygems_version: 3.2.15
108
+ signing_key:
107
109
  specification_version: 4
108
110
  summary: Event System for Rails
109
111
  test_files: []