stance 0.1.0 → 0.5.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 593f0e612e270f03cdacbe2338c181287094558be03d721588db3ab54b094f52
4
- data.tar.gz: 20fb411cd404fa4762e195c44a4a730670ea07862499d50b12f855b41d97cc22
3
+ metadata.gz: 6b55abfe34c74dfe170cc5adda3b174c120bc7b953f174c815350145954697de
4
+ data.tar.gz: 0b407ba5c2fc0f5784ef3489f676f0894d8116211a097096cba8a4c41030c785
5
5
  SHA512:
6
- metadata.gz: cdd07c0676ff44d4ebf2137747d573992c7dc7015c00b3520452aa2478aad46fc4bbf94b8c9d22cd90380a10dad9be96ab091945e1842e3f0529c2d7f70641ab
7
- data.tar.gz: 1b1939a3e0b0686172b4a0edf7fa91cea2fac0a5c7257a18f7003f117bd43674d772692643f9d8bfdc96776a8f00eb8c451b6451a9c911e18673e591942245ce
6
+ metadata.gz: ff33b9da2fd7cf166f7ecb316c1afe04cc61ffefd140a1c4b8164443258673e2f6c440757e90706700cfd7b2b07a830ac34a8c9f039394308f214e5a246922c3
7
+ data.tar.gz: 587bdd935ea27aaff3df1201691c21d7340e1fea876c7857c94d17f94af86013816986cd4efbfcefcfc87ac3355fd510c2ea3c1c2e74d02765eeac447cfde9c2
data/Gemfile CHANGED
@@ -10,6 +10,7 @@ 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'
15
16
  gem 'sqlite3'
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- stance (0.1.0)
4
+ stance (0.5.0)
5
5
  activerecord (>= 5)
6
6
  multi_json
7
7
  railties (>= 5)
@@ -9,48 +9,48 @@ 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.3.1)
13
+ actionview (= 6.1.3.1)
14
+ activesupport (= 6.1.3.1)
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.3.1)
20
+ activesupport (= 6.1.3.1)
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.3.1)
26
+ activesupport (= 6.1.3.1)
27
+ activerecord (6.1.3.1)
28
+ activemodel (= 6.1.3.1)
29
+ activesupport (= 6.1.3.1)
30
+ activesupport (6.1.3.1)
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.8)
44
44
  crass (1.0.6)
45
- erubi (1.9.0)
46
- i18n (1.8.3)
45
+ erubi (1.10.0)
46
+ i18n (1.8.10)
47
47
  concurrent-ruby (~> 1.0)
48
- loofah (2.6.0)
48
+ loofah (2.9.1)
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.0)
53
+ minitest (5.14.4)
54
54
  minitest-autotest (1.1.1)
55
55
  minitest-server (~> 1.0)
56
56
  path_expander (~> 1.0)
@@ -58,13 +58,16 @@ GEM
58
58
  minitest (>= 4, < 6)
59
59
  minitest-server (1.0.6)
60
60
  minitest (~> 5.0)
61
- multi_json (1.14.1)
62
- nokogiri (1.10.9)
63
- mini_portile2 (~> 2.4.0)
64
- parallel (1.19.2)
65
- parser (2.7.1.4)
61
+ mocha (1.12.0)
62
+ multi_json (1.15.0)
63
+ nokogiri (1.11.3)
64
+ mini_portile2 (~> 2.5.0)
65
+ racc (~> 1.4)
66
+ parallel (1.20.1)
67
+ parser (3.0.1.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,34 @@ 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.3.1)
80
+ actionpack (= 6.1.3.1)
81
+ activesupport (= 6.1.3.1)
79
82
  method_source
80
83
  rake (>= 0.8.7)
81
- thor (>= 0.20.3, < 2.0)
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.86.0)
86
+ rake (13.0.3)
87
+ regexp_parser (2.1.1)
88
+ rexml (3.2.5)
89
+ rubocop (1.12.1)
87
90
  parallel (~> 1.10)
88
- parser (>= 2.7.0.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.0.3, < 1.0)
95
+ rubocop-ast (>= 1.2.0, < 2.0)
93
96
  ruby-progressbar (~> 1.7)
94
- unicode-display_width (>= 1.4.0, < 2.0)
95
- rubocop-ast (0.0.3)
96
- parser (>= 2.7.0.1)
97
- ruby-progressbar (1.10.1)
97
+ unicode-display_width (>= 1.4.0, < 3.0)
98
+ rubocop-ast (1.4.1)
99
+ parser (>= 2.7.1.5)
100
+ ruby-progressbar (1.11.0)
98
101
  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.3.0)
102
+ thor (1.1.0)
103
+ tzinfo (2.0.4)
104
+ concurrent-ruby (~> 1.0)
105
+ unicode-display_width (2.0.0)
106
+ zeitwerk (2.4.2)
105
107
 
106
108
  PLATFORMS
107
109
  ruby
@@ -112,10 +114,11 @@ DEPENDENCIES
112
114
  minitest
113
115
  minitest-autotest
114
116
  minitest-focus
115
- rake (~> 12.0)
117
+ mocha
118
+ rake (~> 13.0)
116
119
  rubocop
117
120
  sqlite3
118
121
  stance!
119
122
 
120
123
  BUNDLED WITH
121
- 2.1.4
124
+ 2.2.9
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
@@ -10,25 +18,62 @@ class AppointmentEvents < Stance::Events
10
18
  event 'offers.create'
11
19
  event 'offers.delete'
12
20
 
13
- # Singleton event: only one event with this name can exist for the same subject.
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
+
30
+ # Will be called before/after each event in this class. Have access to the event `subject` and
31
+ # `record`.
32
+ before_create :do_something_before
33
+ after_create :do_something_after
34
+
16
35
  # Optionally, create a class for an event.
17
36
  class SomeEvent < Stance::Event
18
- # Return false if you do not want the event to be created.
19
- def callable?
20
- false
21
- end
22
-
23
- def call
24
- # do something when the event is created.
25
- end
37
+ # Define optional callbacks which have access to the `subject` and event `record`.
38
+ before_create :do_something_before
39
+ after_create :do_something_after
26
40
  end
27
41
  end
28
42
 
29
43
  # Publish events from the model
30
44
  Appointment.find(1).publish_event :some_event
31
45
  Appointment.find(1).publish_event 'offers.create'
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
32
77
  ```
33
78
 
34
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
@@ -6,7 +6,19 @@ require 'stance/engine'
6
6
  module Stance
7
7
  class EventNotFound < StandardError; end
8
8
 
9
+ mattr_accessor :disabled_events
10
+ @@disabled_events = []
11
+
12
+ def self.disable(*events)
13
+ disabled_events.concat events
14
+ yield
15
+ ensure
16
+ self.disabled_events -= events
17
+ end
18
+
9
19
  autoload :Events, 'stance/events'
10
20
  autoload :Event, 'stance/event'
11
21
  autoload :Eventable, 'stance/eventable'
22
+ autoload :ActiveRecordCallbacks, 'stance/active_record_callbacks'
23
+ autoload :ActiveRecordEvents, 'stance/active_record_events'
12
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[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
@@ -2,29 +2,65 @@
2
2
 
3
3
  module Stance
4
4
  class Event
5
+ include ActiveSupport::Callbacks
6
+
7
+ define_callbacks :create
5
8
  attr_reader :record, :options
6
9
 
7
10
  delegate :subject, :name, to: :record
8
11
 
12
+ class << self
13
+ def before_create(*methods, &block)
14
+ set_callback :create, :before, *methods, &block
15
+ end
16
+
17
+ def after_create(*methods, &block)
18
+ set_callback :create, :after, *methods, &block
19
+ end
20
+ end
21
+
9
22
  def initialize(name, subject, metadata, options)
10
- @options = { singleton: false }.merge(options)
11
- @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)
12
32
  end
13
33
 
14
- def valid?
15
- # If event is a singleton, check there is no other active event with the same name. If there
16
- # is, return false.
17
- return false if options[:singleton] && subject.events.active.exists?(name: name)
34
+ def create
35
+ return self if singleton_exists?
18
36
 
19
- callable? && record.save
37
+ Rails.logger.info "Event: #{full_name}"
38
+
39
+ Stance::EventRecord.transaction do
40
+ run_callbacks :create do
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]
49
+ end
50
+ end
51
+
52
+ self
20
53
  end
21
54
 
22
- def callable?
23
- true
55
+ def full_name
56
+ "#{record.subject_type.downcase}.#{name}"
24
57
  end
25
58
 
26
- def call
27
- true
59
+ private
60
+
61
+ # Event is a singleton and already exists.
62
+ def singleton_exists?
63
+ options[:singleton] && subject.events.active.exists?(name: name)
28
64
  end
29
65
  end
30
66
  end
@@ -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
- Stance::EventRecord.transaction do
25
- ev.valid? && ev.call
26
- end
27
- end
60
+ return ev if Stance.disabled_events.include?(ev.full_name)
28
61
 
29
- private
62
+ events_class.new(ev).run_callbacks(:create) { ev.create }
63
+ end
30
64
 
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
data/lib/stance/events.rb CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  module Stance
4
4
  class Events
5
+ include ActiveSupport::Callbacks
6
+
7
+ attr_reader :event
8
+
9
+ define_callbacks :create
10
+
5
11
  class << self
6
12
  attr_reader :events
7
13
 
@@ -9,6 +15,18 @@ module Stance
9
15
  @events ||= {}
10
16
  @events[name.to_s] = options
11
17
  end
18
+
19
+ def before_create(*methods, &block)
20
+ set_callback :create, :before, *methods, &block
21
+ end
22
+
23
+ def after_create(*methods, &block)
24
+ set_callback :create, :after, *methods, &block
25
+ end
26
+ end
27
+
28
+ def initialize(event)
29
+ @event = event
12
30
  end
13
31
  end
14
32
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Stance
4
- VERSION = '0.1.0'
4
+ VERSION = '0.5.1'
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.1.0
4
+ version: 0.5.1
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-06-24 00:00:00.000000000 Z
11
+ date: 2021-04-14 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.3
108
+ signing_key:
107
109
  specification_version: 4
108
110
  summary: Event System for Rails
109
111
  test_files: []