signal 1.1.0 → 1.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 03e8ba417f4a98d92feeef3793b692fb4adec8eb
4
- data.tar.gz: c0d5bdfa9dc3e51ee050009b8d4164ca0622c91f
2
+ SHA256:
3
+ metadata.gz: 377390777df52ac155d1c57d345eb6ca0724fa37a6263a4b71eae40410e134cf
4
+ data.tar.gz: 19bacbd93f6dfaa9cdc40d5ca309316fe5e6579c43f9b78cd58bde8cb0258af9
5
5
  SHA512:
6
- metadata.gz: d1f56b758b92d0786a7808a75f7765fe8c612c8d26183627e5401a7eebfb02cbd78bb597c7249f42edd532e73283d3ca39fd8fbf626e010489d141aa72350f5a
7
- data.tar.gz: f2219aa9e7d6551deceb844a3b302313e254126495cd369561e3f2f194a79acd5aadd487e8003143e35038b93783e94835d12fc14334baf865dc98a1806d7c9f
6
+ metadata.gz: de9c09ac79e17d4d81f39d05182fd4a59d38e6beb275cda6eb63d1dc3da586d98340407332ec815085d5848cb55f35a0f147e9d72bb7ea514a46443a5c7c2125
7
+ data.tar.gz: 3a87059e3e2cb9b62fb728c1dfad67fa7b7fb128776854530939af10aa2aa5957e3defd75895f203f6befe152a4ce50aae6e78e33afa52988eeaf41c91b51ce8
@@ -0,0 +1,12 @@
1
+ ---
2
+ inherit_gem:
3
+ rubocop-fnando: .rubocop.yml
4
+
5
+ AllCops:
6
+ TargetRubyVersion: 2.7
7
+
8
+ Metrics/AbcSize:
9
+ Enabled: false
10
+
11
+ Metrics/MethodLength:
12
+ Enabled: false
@@ -3,12 +3,15 @@ cache: bundler
3
3
  sudo: false
4
4
  script: bundle exec rake
5
5
  rvm:
6
- - '2.3.0'
7
- - '2.2'
8
- - '2.1'
9
- addons:
10
- code_climate:
11
- repo_token:
12
- secure: "ASogQTLS/xYYczRtFwuo8DIkNdOl16mez4GHOw4RtdfTQ4BMROxE/CUemdPulMW1b/p7jsp5ghOtH2EJ4jN203s83HN4QjGC2wXhigkk6LzvxqjkmHxIWxJi2JS2sLq8nzOCcD0geO1pcbBFyuAzAdlwWa2g1WDP7O0L0fNjCtM="
6
+ - 2.4.2
7
+ before_script:
8
+ - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
9
+ - chmod +x ./cc-test-reporter
10
+ - "./cc-test-reporter before-build"
11
+ after_script:
12
+ - "./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT"
13
13
  notifications:
14
14
  email: false
15
+ env:
16
+ global:
17
+ secure: EmzkOnefLJlsAbh4tKzcdfX9PiwQMNSmWz1aTaieA1C9GJryT2JswBa/eVgU3jDKPhFFdZ5X8K6bf0ArAdDXFMW5+DPQ9nWDOCUH3QuuFTOicyTi16W8HCozbLTg49CdB0kP4iQlA3rdpje/T9zgsTvdNDuBevB90JplHy5Lu6o=
data/Gemfile CHANGED
@@ -1,2 +1,4 @@
1
- source 'http://rubygems.org'
1
+ # frozen_string_literal: true
2
+
3
+ source "http://rubygems.org"
2
4
  gemspec
data/README.md CHANGED
@@ -4,7 +4,8 @@
4
4
  [![Code Climate](https://codeclimate.com/github/fnando/signal/badges/gpa.svg)](https://codeclimate.com/github/fnando/signal)
5
5
  [![Test Coverage](https://codeclimate.com/github/fnando/signal/badges/coverage.svg)](https://codeclimate.com/github/fnando/signal)
6
6
 
7
- A simple observer implementation on POROs (Plain Old Ruby Object) and ActiveRecord objects.
7
+ A simple observer implementation on POROs (Plain Old Ruby Object) and
8
+ ActiveRecord objects.
8
9
 
9
10
  ## Installation
10
11
 
@@ -28,7 +29,8 @@ You can use Signal with PORO (Plain Old Ruby Object) and ActiveRecord.
28
29
 
29
30
  ### Plain Ruby
30
31
 
31
- All you have to do is including the `Signal` module. Then you can add listeners and trigger events.
32
+ All you have to do is including the `Signal` module. Then you can add listeners
33
+ and trigger events.
32
34
 
33
35
  ```ruby
34
36
  class Status
@@ -49,7 +51,8 @@ status.ready!
49
51
  #=> After the ready event!
50
52
  ```
51
53
 
52
- You can also pass objects that implement methods like `before_*`, `on_*` and `after_*`.
54
+ You can also pass objects that implement methods like `before_*`, `on_*` and
55
+ `after_*`.
53
56
 
54
57
  ```ruby
55
58
  class MyListener
@@ -74,7 +77,9 @@ Status.new
74
77
  #=> After the ready event!
75
78
  ```
76
79
 
77
- Executed blocks don't switch context. You always have to emit the object you're interested in. The follow example uses `emit(:output, self)` to send the `Contact` instance to all listeners.
80
+ Executed blocks don't switch context. You always have to emit the object you're
81
+ interested in. The follow example uses `emit(:output, self)` to send the
82
+ `Contact` instance to all listeners.
78
83
 
79
84
  ```ruby
80
85
  class Contact
@@ -119,7 +124,8 @@ Arguments.new
119
124
 
120
125
  ### ActiveRecord
121
126
 
122
- You can use Signal with ActiveRecord, which will give you some default events like `:create`, `:update`, `:remove` and `:validation`.
127
+ You can use Signal with ActiveRecord, which will give you some default events
128
+ like `:create`, `:update`, `:remove` and `:validation`.
123
129
 
124
130
  ```ruby
125
131
  class Thing < ActiveRecord::Base
@@ -151,22 +157,23 @@ thing.destroy
151
157
 
152
158
  These are the available events:
153
159
 
154
- * `before(:create)`: triggered before creating the record (record is valid).
155
- * `on(:create)`: triggered after `before(:create)` event.
156
- * `after(:create)`: triggered after the `on(:create)` event.
157
- * `before(:update)`: triggered before updating the record (record is valid).
158
- * `on(:update)`: triggered when the `before(:update)` event.
159
- * `after(:update)`: triggered after the `on(:update)` event.
160
- * `before(:remove)`: triggered before removing the record.
161
- * `on(:remove)`: triggered after the `before(:remove)`.
162
- * `after(:remove)`: triggered after the `on(:remove)` event.
163
- * `before(:validation)`: triggered before validating record.
164
- * `on(:validation)`: triggered when record is invalid.
165
- * `after(:validation)`: triggered after validating record.
160
+ - `before(:create)`: triggered before creating the record (record is valid).
161
+ - `on(:create)`: triggered after `before(:create)` event.
162
+ - `after(:create)`: triggered after the `on(:create)` event.
163
+ - `before(:update)`: triggered before updating the record (record is valid).
164
+ - `on(:update)`: triggered when the `before(:update)` event.
165
+ - `after(:update)`: triggered after the `on(:update)` event.
166
+ - `before(:remove)`: triggered before removing the record.
167
+ - `on(:remove)`: triggered after the `before(:remove)`.
168
+ - `after(:remove)`: triggered after the `on(:remove)` event.
169
+ - `before(:validation)`: triggered before validating record.
170
+ - `on(:validation)`: triggered when record is invalid.
171
+ - `after(:validation)`: triggered after validating record.
166
172
 
167
173
  ### Inside Rails
168
174
 
169
- Although there's no special code for Rails, here's just an example of how you can use it:
175
+ Although there's no special code for Rails, here's just an example of how you
176
+ can use it:
170
177
 
171
178
  ```ruby
172
179
  class UsersController < ApplicationController
@@ -197,7 +204,10 @@ end
197
204
 
198
205
  ### Signal::Call
199
206
 
200
- You can include `Signal.call` instead, so you can have a common interface for your observable object. This will add the `.call()` method to the target class, which will delegate attributes to the observable's `initialize` method and call its `call` method.
207
+ You can include `Signal.call` instead, so you can have a common interface for
208
+ your observable object. This will add the `.call()` method to the target class,
209
+ which will delegate attributes to the observable's `initialize` method and call
210
+ its `call` method.
201
211
 
202
212
  ```ruby
203
213
  class Contact
@@ -219,7 +229,43 @@ Contact.call('John', 'john@example.com') do |o|
219
229
  end
220
230
  ```
221
231
 
222
- Notice that you don't have to explicit call the instance's `call` method; `Contact.call` will initialize the object with all the provided parameters and call `Contact#call` after the block has been executed.
232
+ Notice that you don't have to explicit call the instance's `call` method;
233
+ `Contact.call` will initialize the object with all the provided parameters and
234
+ call `Contact#call` after the block has been executed.
235
+
236
+ ### Testing
237
+
238
+ `Signal::Mock` can be helpful for most test situations where you don't want to
239
+ bring other mock libraries.
240
+
241
+ ```ruby
242
+ require "signal/mock"
243
+
244
+ class SomeTest < Minitest::Test
245
+ def test_some_test
246
+ mock = Signal::Mock.new
247
+
248
+ # Using listener
249
+ sum = Sum.new
250
+ sum.add_listener(mock)
251
+
252
+ # Calling `mock.on(event_name)` is required because
253
+ # the handler doesn't receive the event name, just the
254
+ # arguments.
255
+ sum = Sum.new
256
+ sum.on(:result, &mock.on(:result))
257
+
258
+ # Using with Signal.call
259
+ Sum.call(1, 2, &mock)
260
+
261
+ assert mock.received?(:result)
262
+ assert mock.received?(:result, times: 1)
263
+ assert mock.received?(:result, with: [3])
264
+ assert mock.received?(:result, with: ->(result) { result == 3 } )
265
+ end
266
+ end
267
+
268
+ ```
223
269
 
224
270
  ## Contributing
225
271
 
@@ -231,25 +277,23 @@ Notice that you don't have to explicit call the instance's `call` method; `Conta
231
277
 
232
278
  ## License
233
279
 
234
- Copyright (c) 2013-2015 Nando Vieira
280
+ Copyright (c) 2013 Nando Vieira
235
281
 
236
282
  MIT License
237
283
 
238
- Permission is hereby granted, free of charge, to any person obtaining
239
- a copy of this software and associated documentation files (the
240
- "Software"), to deal in the Software without restriction, including
241
- without limitation the rights to use, copy, modify, merge, publish,
242
- distribute, sub-license, and/or sell copies of the Software, and to
243
- permit persons to whom the Software is furnished to do so, subject to
244
- the following conditions:
245
-
246
- The above copyright notice and this permission notice shall be
247
- included in all copies or substantial portions of the Software.
248
-
249
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
250
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
251
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
252
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
253
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
254
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
255
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
284
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
285
+ this software and associated documentation files (the "Software"), to deal in
286
+ the Software without restriction, including without limitation the rights to
287
+ use, copy, modify, merge, publish, distribute, sub-license, and/or sell copies
288
+ of the Software, and to permit persons to whom the Software is furnished to do
289
+ so, subject to the following conditions:
290
+
291
+ The above copyright notice and this permission notice shall be included in all
292
+ copies or substantial portions of the Software.
293
+
294
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
295
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
296
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
297
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
298
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
299
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile CHANGED
@@ -1,10 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "bundler/gem_tasks"
2
4
  require "rake/testtask"
3
5
 
4
6
  Rake::TestTask.new(:test) do |t|
5
- t.libs << 'test'
6
- t.test_files = FileList['test/**/*_test.rb']
7
+ t.libs << "test"
8
+ t.test_files = FileList["test/**/*_test.rb"]
7
9
  t.warning = false
8
10
  end
9
11
 
10
- task :default => :test
12
+ task default: :test
@@ -1,13 +1,15 @@
1
- $:.unshift File.expand_path("../../lib", __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
2
4
  require "signal"
3
5
  require "active_record"
4
6
 
5
- ActiveRecord::Base.establish_connection({
6
- :adapter => "sqlite3",
7
- :database => ":memory:"
8
- })
7
+ ActiveRecord::Base.establish_connection(
8
+ adapter: "sqlite3",
9
+ database: ":memory:"
10
+ )
9
11
 
10
- ActiveRecord::Schema.define(:version => 0) do
12
+ ActiveRecord::Schema.define(version: 0) do
11
13
  create_table :things do |t|
12
14
  t.string :name
13
15
  t.timestamps null: false
@@ -20,7 +22,7 @@ class Thing < ActiveRecord::Base
20
22
  validates_presence_of :name
21
23
  end
22
24
 
23
- thing = Thing.new(:name => "Stuff")
25
+ thing = Thing.new(name: "Stuff")
24
26
  thing.on(:create) {|model| puts model.updated_at, model.name }
25
27
  thing.on(:update) {|model| puts model.updated_at, model.name }
26
28
  thing.on(:remove) {|model| puts model.destroyed? }
@@ -30,11 +32,11 @@ thing.save!
30
32
  #=> 2013-01-26 10:32:39 -0200
31
33
  #=> Stuff
32
34
 
33
- thing.update_attributes(:name => "Updated stuff")
35
+ thing.update_attributes(name: "Updated stuff")
34
36
  #=> 2013-01-26 10:33:11 -0200
35
37
  #=> Updated stuff
36
38
 
37
- thing.update_attributes(:name => nil)
39
+ thing.update_attributes(name: nil)
38
40
  #=> ["Name can"t be blank"]
39
41
 
40
42
  thing.destroy
@@ -1,4 +1,6 @@
1
- $:.unshift File.expand_path("../../lib", __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
2
4
  require "signal"
3
5
 
4
6
  class Arguments
@@ -1,4 +1,6 @@
1
- $:.unshift File.expand_path("../../lib", __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
2
4
  require "signal"
3
5
 
4
6
  class Contact
@@ -7,7 +9,8 @@ class Contact
7
9
  attr_reader :name, :email
8
10
 
9
11
  def initialize(name, email)
10
- @name, @email = name, email
12
+ @name = name
13
+ @email = email
11
14
  end
12
15
 
13
16
  def output!
@@ -16,5 +19,5 @@ class Contact
16
19
  end
17
20
 
18
21
  contact = Contact.new("John Doe", "john@example.org")
19
- contact.on(:output) {|contact| puts contact.name, contact.email }
22
+ contact.on(:output) {|c| puts c.name, c.email }
20
23
  contact.output!
@@ -1,4 +1,6 @@
1
- $:.unshift File.expand_path("../../lib", __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
2
4
  require "signal"
3
5
 
4
6
  class Status
@@ -1,4 +1,6 @@
1
- $:.unshift File.expand_path("../../lib", __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
2
4
  require "signal"
3
5
 
4
6
  class Contact
@@ -7,7 +9,8 @@ class Contact
7
9
  attr_reader :name, :email
8
10
 
9
11
  def initialize(name, email)
10
- @name, @email = name, email
12
+ @name = name
13
+ @email = email
11
14
  end
12
15
 
13
16
  def call
@@ -1,13 +1,15 @@
1
- $:.unshift File.expand_path("../../lib", __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
2
4
  require "signal"
3
5
  require "active_record"
4
6
 
5
- ActiveRecord::Base.establish_connection({
6
- :adapter => "sqlite3",
7
- :database => ":memory:"
8
- })
7
+ ActiveRecord::Base.establish_connection(
8
+ adapter: "sqlite3",
9
+ database: ":memory:"
10
+ )
9
11
 
10
- ActiveRecord::Schema.define(:version => 0) do
12
+ ActiveRecord::Schema.define(version: 0) do
11
13
  create_table :things do |t|
12
14
  t.string :name
13
15
  t.timestamps null: false
@@ -21,8 +23,8 @@ class Thing < ActiveRecord::Base
21
23
  end
22
24
 
23
25
  class MyListener
24
- [:validation, :update, :create, :remove].each do |type|
25
- class_eval <<-RUBY
26
+ %i[validation update create remove].each do |type|
27
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
26
28
  def before_#{type}(thing); puts __method__; end
27
29
  def on_#{type}(thing); puts __method__; end
28
30
  def after_#{type}(thing); puts __method__; end
@@ -31,26 +33,26 @@ class MyListener
31
33
  end
32
34
 
33
35
  puts "\n=== Creating valid record"
34
- thing = Thing.new(:name => "Stuff")
36
+ thing = Thing.new(name: "Stuff")
35
37
  thing.listeners << MyListener.new
36
38
  thing.save
37
39
 
38
40
  puts "\n=== Creating invalid record"
39
- thing = Thing.new(:name => nil)
41
+ thing = Thing.new(name: nil)
40
42
  thing.listeners << MyListener.new
41
43
  thing.save
42
44
 
43
45
  puts "\n=== Updating valid record"
44
- thing = Thing.create(:name => "Stuff")
46
+ thing = Thing.create(name: "Stuff")
45
47
  thing.listeners << MyListener.new
46
- thing.update_attributes(:name => "Updated stuff")
48
+ thing.update_attributes(name: "Updated stuff")
47
49
 
48
50
  puts "\n=== Updating invalid record"
49
- thing = Thing.create!(:name => "Stuff")
51
+ thing = Thing.create!(name: "Stuff")
50
52
  thing.listeners << MyListener.new
51
- thing.update_attributes(:name => nil)
53
+ thing.update_attributes(name: nil)
52
54
 
53
55
  puts "\n=== Removing record"
54
- thing = Thing.create(:name => "Stuff")
56
+ thing = Thing.create(name: "Stuff")
55
57
  thing.listeners << MyListener.new
56
58
  thing.destroy
@@ -1,4 +1,6 @@
1
- $:.unshift File.expand_path("../../lib", __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
2
4
  require "signal"
3
5
 
4
6
  class Status
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "signal/version"
2
4
  require "signal/listener"
3
5
  require "signal/extensions/active_record"
@@ -35,11 +37,13 @@ module Signal
35
37
  @listeners ||= []
36
38
  end
37
39
 
38
- private
39
- def emit_signal(type, event, *args)
40
+ private def emit_signal(type, event, *args)
40
41
  listeners.each do |listener|
41
42
  method_name = "#{type}_#{event}"
42
- listener.send(method_name, *args) if listener.respond_to?(method_name, true)
43
+
44
+ if listener.respond_to?(method_name, true)
45
+ listener.send(method_name, *args)
46
+ end
43
47
  end
44
48
  end
45
49
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Signal
2
4
  def self.active_record
3
5
  Extensions::ActiveRecord
@@ -17,16 +19,16 @@ module Signal
17
19
  end
18
20
  end
19
21
 
20
- private
21
- def around_create_signal
22
+ private def around_create_signal
22
23
  emit_signal(:before, :create, self)
23
24
  yield
24
25
  return unless persisted?
26
+
25
27
  emit_signal(:on, :create, self)
26
28
  emit_signal(:after, :create, self)
27
29
  end
28
30
 
29
- def around_save_signal
31
+ private def around_save_signal
30
32
  if new_record?
31
33
  yield
32
34
  return
@@ -38,18 +40,18 @@ module Signal
38
40
  emit_signal(:after, :update, self)
39
41
  end
40
42
 
41
- def around_destroy_signal
43
+ private def around_destroy_signal
42
44
  emit_signal(:before, :remove, self)
43
45
  yield
44
46
  emit_signal(:on, :remove, self)
45
47
  emit_signal(:after, :remove, self)
46
48
  end
47
49
 
48
- def before_validation_signal
50
+ private def before_validation_signal
49
51
  emit_signal(:before, :validation, self)
50
52
  end
51
53
 
52
- def after_validation_signal
54
+ private def after_validation_signal
53
55
  emit_signal(:on, :validation, self) if errors.any?
54
56
  emit_signal(:after, :validation, self)
55
57
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Signal
2
4
  def self.call
3
5
  Extensions::Call
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Signal
2
4
  class Listener
3
5
  def initialize(context, type, event, &block)
@@ -10,6 +12,7 @@ module Signal
10
12
 
11
13
  def method_missing(method_name, *args)
12
14
  return super unless respond_to_missing?(method_name, false)
15
+
13
16
  @block.call(*args)
14
17
  end
15
18
 
@@ -17,7 +20,7 @@ module Signal
17
20
  "<#{self.class} event: #{@event_method}>"
18
21
  end
19
22
 
20
- def respond_to_missing?(method_name, include_private)
23
+ def respond_to_missing?(method_name, _include_private)
21
24
  method_name == @event_method
22
25
  end
23
26
  end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Signal
4
+ class Mock
5
+ def calls
6
+ @calls ||= []
7
+ end
8
+
9
+ def method_missing(name, *args)
10
+ return super unless respond_to_missing?(name)
11
+
12
+ calls << {event: name.to_s.gsub(/^on_/, "").to_sym, args: args}
13
+ end
14
+
15
+ def respond_to_missing?(name, _include_all = false)
16
+ name =~ /^on_/
17
+ end
18
+
19
+ def received?(event, options = {})
20
+ received_event?(event, options[:times] || -1) &&
21
+ received_with?(event, options[:with])
22
+ end
23
+
24
+ def to_proc
25
+ proc {|action| action.add_listener(self) }
26
+ end
27
+
28
+ def on(event)
29
+ proc {|*args| calls << {event: event, args: args} }
30
+ end
31
+
32
+ private def received_event?(event, count)
33
+ received_calls = calls.select {|call| call[:event] == event }.size
34
+
35
+ return received_calls.nonzero? if count == -1
36
+
37
+ received_calls == count
38
+ end
39
+
40
+ private def received_with?(event, args)
41
+ return true unless args
42
+
43
+ calls.any? do |call|
44
+ next unless call[:event] == event
45
+ next args.call(call[:args]) if args.is_a?(Proc)
46
+
47
+ args == call[:args]
48
+ end
49
+ end
50
+ end
51
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Signal
2
- VERSION = "1.1.0"
4
+ VERSION = "1.2.0"
3
5
  end
@@ -1,22 +1,33 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "./lib/signal/version"
2
4
 
3
- Gem::Specification.new do |gem|
4
- gem.name = "signal"
5
- gem.version = Signal::VERSION
6
- gem.authors = ["Nando Vieira"]
7
- gem.email = ["fnando.vieira@gmail.com"]
8
- gem.description = "A simple observer implementation for POROs (Plain Old Ruby Object) and ActiveRecord objects."
9
- gem.summary = gem.description
10
- gem.homepage = "http://github.com/fnando/signal"
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "signal"
7
+ spec.version = Signal::VERSION
8
+ spec.authors = ["Nando Vieira"]
9
+ spec.email = ["fnando.vieira@gmail.com"]
10
+ spec.description = %w[
11
+ A simple observer implementation for POROs (Plain Old Ruby Object) and
12
+ ActiveRecord objects.
13
+ ].join(" ")
14
+ spec.summary = spec.description
15
+ spec.homepage = "http://github.com/fnando/signal"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
+ f.match(%r{^(test|spec|features)/})
19
+ end
11
20
 
12
- gem.files = `git ls-files`.split($/)
13
- gem.executables = gem.files.grep(%r{^bin/}).map {|f| File.basename(f) }
14
- gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
15
- gem.require_paths = ["lib"]
21
+ spec.executables = spec.files.grep(%r{^bin/}).map {|f| File.basename(f) }
22
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
23
+ spec.require_paths = ["lib"]
16
24
 
17
- gem.add_development_dependency "activerecord"
18
- gem.add_development_dependency "sqlite3"
19
- gem.add_development_dependency "minitest-utils"
20
- gem.add_development_dependency "codeclimate-test-reporter"
21
- gem.add_development_dependency "rake"
25
+ spec.add_development_dependency "activerecord"
26
+ spec.add_development_dependency "minitest-utils"
27
+ spec.add_development_dependency "pry-meta"
28
+ spec.add_development_dependency "rake"
29
+ spec.add_development_dependency "rubocop"
30
+ spec.add_development_dependency "rubocop-fnando"
31
+ spec.add_development_dependency "simplecov"
32
+ spec.add_development_dependency "sqlite3"
22
33
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: signal
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nando Vieira
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-10-18 00:00:00.000000000 Z
11
+ date: 2020-02-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -25,7 +25,7 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: sqlite3
28
+ name: minitest-utils
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
@@ -39,7 +39,7 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: minitest-utils
42
+ name: pry-meta
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - ">="
@@ -53,7 +53,7 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: codeclimate-test-reporter
56
+ name: rake
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - ">="
@@ -67,7 +67,49 @@ dependencies:
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
- name: rake
70
+ name: rubocop
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop-fnando
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
+ - !ruby/object:Gem::Dependency
112
+ name: sqlite3
71
113
  requirement: !ruby/object:Gem::Requirement
72
114
  requirements:
73
115
  - - ">="
@@ -89,8 +131,8 @@ extensions: []
89
131
  extra_rdoc_files: []
90
132
  files:
91
133
  - ".gitignore"
134
+ - ".rubocop.yml"
92
135
  - ".travis.yml"
93
- - CHANGELOG.md
94
136
  - Gemfile
95
137
  - LICENSE.txt
96
138
  - README.md
@@ -106,19 +148,9 @@ files:
106
148
  - lib/signal/extensions/active_record.rb
107
149
  - lib/signal/extensions/call.rb
108
150
  - lib/signal/listener.rb
151
+ - lib/signal/mock.rb
109
152
  - lib/signal/version.rb
110
153
  - signal.gemspec
111
- - test/signal/activerecord_test.rb
112
- - test/signal/call_test.rb
113
- - test/signal/issues_test.rb
114
- - test/signal/listener_test.rb
115
- - test/signal/signal_test.rb
116
- - test/support/callable.rb
117
- - test/support/emitter.rb
118
- - test/support/observable.rb
119
- - test/support/observable_with_call.rb
120
- - test/support/user.rb
121
- - test/test_helper.rb
122
154
  homepage: http://github.com/fnando/signal
123
155
  licenses: []
124
156
  metadata: {}
@@ -137,21 +169,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
137
169
  - !ruby/object:Gem::Version
138
170
  version: '0'
139
171
  requirements: []
140
- rubyforge_project:
141
- rubygems_version: 2.5.1
172
+ rubygems_version: 3.1.2
142
173
  signing_key:
143
174
  specification_version: 4
144
175
  summary: A simple observer implementation for POROs (Plain Old Ruby Object) and ActiveRecord
145
176
  objects.
146
- test_files:
147
- - test/signal/activerecord_test.rb
148
- - test/signal/call_test.rb
149
- - test/signal/issues_test.rb
150
- - test/signal/listener_test.rb
151
- - test/signal/signal_test.rb
152
- - test/support/callable.rb
153
- - test/support/emitter.rb
154
- - test/support/observable.rb
155
- - test/support/observable_with_call.rb
156
- - test/support/user.rb
157
- - test/test_helper.rb
177
+ test_files: []
@@ -1,5 +0,0 @@
1
- # Signal
2
-
3
- ### v1.0.0
4
-
5
- - Use `include Signal.active_record` instead of `include Signal::ActiveRecord`. This fixes problems with [constant lookup](https://github.com/fnando/signal/issues/2).
@@ -1,169 +0,0 @@
1
- require "test_helper"
2
-
3
- class ARCreateEventTest < Minitest::Test
4
- let(:user) { User.new(username: "johndoe") }
5
-
6
- test "triggers before event" do
7
- callable = Callable.new(:before_create)
8
- user.before(:create, &callable)
9
- callable.expect(:before_create, nil, [user])
10
- user.save!
11
-
12
- assert callable.verify
13
- end
14
-
15
- test "triggers event" do
16
- callable = Callable.new(:on_create)
17
- user.on(:create, &callable)
18
- callable.expect(:on_create, nil, [user])
19
- user.save!
20
-
21
- assert callable.verify
22
- end
23
-
24
- test "triggers after event" do
25
- callable = Callable.new(:after_create)
26
- user.after(:create, &callable)
27
- callable.expect(:after_create, nil, [user])
28
- user.save!
29
-
30
- assert callable.verify
31
- end
32
-
33
- test "does not trigger events when record is invalid" do
34
- user = User.new
35
- calls = []
36
-
37
- user
38
- .before(:create) { calls << :before }
39
- .on(:create) { calls << :on }
40
- .after(:create) { calls << :after }
41
-
42
- user.save
43
-
44
- assert_equal [], calls
45
- end
46
- end
47
-
48
- class ARValidationEventTest < Minitest::Test
49
- let(:user) { User.new(username: "johndoe") }
50
-
51
- test "triggers before event" do
52
- callable = Callable.new(:before_validation)
53
- user.before(:validation, &callable)
54
- callable.expect(:before_validation, nil, [user])
55
- user.save!
56
-
57
- assert callable.verify
58
- end
59
-
60
- test "triggers after event" do
61
- callable = Callable.new(:after_validation)
62
- user.after(:validation, &callable)
63
- callable.expect(:after_validation, nil, [user])
64
- user.save!
65
-
66
- assert callable.verify
67
- end
68
-
69
- test "triggers validation event when record is invalid" do
70
- callable = Callable.new(:on_validation)
71
- user.username = nil
72
- user.on(:validation, &callable)
73
-
74
- callable.expect(:on_validation, nil, [user])
75
- user.save
76
-
77
- assert callable.verify
78
- end
79
-
80
- test "skips validation event when record is valid" do
81
- calls = []
82
- user.on(:validation) { calls << :on }
83
- user.save!
84
-
85
- assert_equal [], calls
86
- end
87
- end
88
-
89
- class ARUpdateEvent < Minitest::Test
90
- let(:user) { User.create!(username: "johndoe") }
91
-
92
- test "triggers before event" do
93
- callable = Callable.new(:before_update)
94
- user.before(:update, &callable)
95
-
96
- callable.expect(:before_update, nil, [user])
97
- user.update_attributes!(username: "johnd")
98
-
99
- assert callable.verify
100
- end
101
-
102
- test "triggers event" do
103
- callable = Callable.new(:on_update)
104
- user.on(:update, &callable)
105
-
106
- callable.expect(:on_update, nil, [user])
107
- user.update_attributes!(username: "johnd")
108
-
109
- assert callable.verify
110
- end
111
-
112
- test "triggers after event" do
113
- callable = Callable.new(:after_update)
114
- user.after(:update, &callable)
115
-
116
- callable.expect(:after_update, nil, [user])
117
- user.update_attributes!(username: "johnd")
118
-
119
- assert callable.verify
120
- end
121
-
122
- test "does not trigger events when record is invalid" do
123
- user.username = nil
124
- calls = []
125
-
126
- user
127
- .before(:update) { calls << :before }
128
- .on(:update) { calls << :on }
129
- .after(:update) { calls << :after }
130
-
131
- user.save
132
-
133
- assert_equal [], calls
134
- end
135
- end
136
-
137
- class ARRemoveEvent < Minitest::Test
138
- let(:user) { User.create!(username: "johndoe") }
139
-
140
- test "triggers before event" do
141
- callable = Callable.new(:before_remove)
142
- user.before(:remove, &callable)
143
-
144
- callable.expect(:before_remove, nil, [user])
145
- user.destroy
146
-
147
- assert callable.verify
148
- end
149
-
150
- test "triggers event" do
151
- callable = Callable.new(:on_remove)
152
- user.on(:remove, &callable)
153
-
154
- callable.expect(:on_remove, nil, [user])
155
- user.destroy
156
-
157
- assert callable.verify
158
- end
159
-
160
- test "triggers after event" do
161
- callable = Callable.new(:after_remove)
162
- user.after(:remove, &callable)
163
-
164
- callable.expect(:after_remove, nil, [user])
165
- user.destroy
166
-
167
- assert callable.verify
168
- end
169
- end
@@ -1,19 +0,0 @@
1
- require "test_helper"
2
-
3
- class SignalCallTest < Minitest::Test
4
- test "initializes observable with arguments" do
5
- observable = ObservableWithCall.call(1, 2, 3)
6
- assert_equal [1, 2, 3], observable.args
7
- end
8
-
9
- test "triggers event" do
10
- callable = Callable.new(:on_args)
11
- callable.expect(:on_args, nil, [[1, 2, 3]])
12
-
13
- ObservableWithCall.call(1, 2, 3) do |o|
14
- o.on(:args, &callable)
15
- end
16
-
17
- assert callable.verify
18
- end
19
- end
@@ -1,15 +0,0 @@
1
- require "test_helper"
2
-
3
- # https://github.com/fnando/signal/issues/2
4
- class ConstantLookupTest < Minitest::Test
5
- let(:callable) { Callable.new(:on_success) }
6
-
7
- test "triggers event" do
8
- emitter = Emitter.new
9
- emitter.on(:success, &callable)
10
- callable.expect(:on_success, nil, [])
11
- emitter.call
12
-
13
- assert callable.verify
14
- end
15
- end
@@ -1,8 +0,0 @@
1
- require "test_helper"
2
-
3
- class SignalListenerTest < Minitest::Test
4
- test "sets string representation" do
5
- listener = Signal::Listener.new(self, :on, :ready, &->{})
6
- assert_equal "<Signal::Listener event: on_ready>", listener.to_s
7
- end
8
- end
@@ -1,146 +0,0 @@
1
- require "test_helper"
2
-
3
- class AddListenerTest < Minitest::Test
4
- let(:observable) { Observable.new }
5
- let(:callable) { Callable.new(:on_ready) }
6
-
7
- test "runs listener" do
8
- callable.expect(:on_ready, nil, [])
9
- observable.add_listener(callable)
10
-
11
- observable.emit(:ready)
12
-
13
- assert callable.verify
14
- end
15
-
16
- test "returns itself" do
17
- assert_equal observable, observable.add_listener(callable)
18
- end
19
- end
20
-
21
- class UsingBlocksTest < Minitest::Test
22
- let(:observable) { Observable.new }
23
- let(:callable) { Callable.new(:on_ready) }
24
-
25
- test "triggers event" do
26
- callable.expect(:on_ready, nil, [])
27
- observable.on(:ready, &callable)
28
-
29
- observable.emit(:ready)
30
-
31
- assert callable.verify
32
- end
33
-
34
- test "triggers event with arguments" do
35
- callable.expect(:on_ready, nil, [1, 2, 3])
36
- observable.on(:ready, &callable)
37
-
38
- observable.emit(:ready, 1, 2, 3)
39
-
40
- assert callable.verify
41
- end
42
-
43
- test "triggers before event" do
44
- callable = Callable.new(:before_ready)
45
- callable.expect(:before_ready, nil, [])
46
- observable.before(:ready, &callable)
47
-
48
- observable.emit(:ready)
49
-
50
- assert callable.verify
51
- end
52
-
53
- test "triggers before event with arguments" do
54
- callable = Callable.new(:before_ready)
55
- callable.expect(:before_ready, nil, [1, 2, 3])
56
- observable.before(:ready, &callable)
57
-
58
- observable.emit(:ready, 1, 2, 3)
59
-
60
- assert callable.verify
61
- end
62
-
63
- test "triggers after event" do
64
- callable = Callable.new(:after_ready)
65
- callable.expect(:after_ready, nil, [])
66
- observable.after(:ready, &callable)
67
-
68
- observable.emit(:ready)
69
-
70
- assert callable.verify
71
- end
72
-
73
- test "triggers after event with arguments" do
74
- callable = Callable.new(:after_ready)
75
- callable.expect(:after_ready, nil, [1, 2, 3])
76
- observable.after(:ready, &callable)
77
-
78
- observable.emit(:ready, 1, 2, 3)
79
-
80
- assert callable.verify
81
- end
82
-
83
- test "chains events" do
84
- calls = []
85
-
86
- observable
87
- .before(:ready) { calls << :before }
88
- .on(:ready) { calls << :on }
89
- .after(:ready) { calls << :after }
90
-
91
- observable.emit(:ready)
92
-
93
- assert_equal %i[before on after], calls
94
- end
95
-
96
- test "keeps context" do
97
- context = nil
98
- callable = -> { context = self }
99
- observable.on(:ready, &callable)
100
- observable.emit(:ready)
101
-
102
- assert_equal self, context
103
- end
104
- end
105
-
106
- class UsingListenersTest < Minitest::Test
107
- let(:observable) { Observable.new }
108
- let(:callable) { Callable.new(:on_ready) }
109
-
110
- test "triggers event for listener" do
111
- observable.listeners << callable
112
- callable.expect(:after_ready, nil, [])
113
-
114
- observable.emit(:ready)
115
-
116
- assert callable.verify
117
- end
118
-
119
- test "triggers event for listener with arguments" do
120
- observable.listeners << callable
121
- callable.expect(:after_ready, nil, [1, 2, 3])
122
-
123
- observable.emit(:ready, 1, 2, 3)
124
-
125
- assert callable.verify
126
- end
127
- end
128
-
129
- class MixedListenersTest < Minitest::Test
130
- test "calls all listeners" do
131
- observable = Observable.new
132
- callable = Callable.new(:on_ready)
133
- another_callable = Callable.new(:on_ready)
134
-
135
- callable.expect(:on_ready, nil, [])
136
- another_callable.expect(:on_ready, nil, [])
137
-
138
- observable
139
- .add_listener(callable)
140
- .on(:ready, &another_callable)
141
- .emit(:ready)
142
-
143
- assert callable.verify
144
- assert another_callable.verify
145
- end
146
- end
@@ -1,11 +0,0 @@
1
- class Callable < Minitest::Mock
2
- def initialize(method_name)
3
- super()
4
- @method_name = method_name
5
- end
6
-
7
- def to_proc
8
- callable = self
9
- proc {|*args| callable.send(@method_name, *args) }
10
- end
11
- end
@@ -1,9 +0,0 @@
1
- class Emitter
2
- include Signal
3
-
4
- def call
5
- ActiveRecord::Base.transaction do
6
- emit(:success)
7
- end
8
- end
9
- end
@@ -1,3 +0,0 @@
1
- class Observable
2
- include Signal
3
- end
@@ -1,13 +0,0 @@
1
- class ObservableWithCall
2
- include Signal.call
3
-
4
- attr_reader :args
5
-
6
- def initialize(*args)
7
- @args = args
8
- end
9
-
10
- def call
11
- emit(:args, @args)
12
- end
13
- end
@@ -1,4 +0,0 @@
1
- class User < ActiveRecord::Base
2
- include Signal.active_record
3
- validates_presence_of :username
4
- end
@@ -1,28 +0,0 @@
1
- require "codeclimate-test-reporter"
2
- CodeClimate::TestReporter.start
3
-
4
- require "bundler/setup"
5
- require "active_record"
6
-
7
- I18n.enforce_available_locales = false
8
-
9
- require "minitest/utils"
10
- require "minitest/autorun"
11
-
12
- require "signal"
13
- require "support/observable"
14
- require "support/observable_with_call"
15
- require "support/user"
16
- require "support/callable"
17
- require "support/emitter"
18
-
19
- ActiveRecord::Base.establish_connection({
20
- :adapter => "sqlite3",
21
- :database => ":memory:"
22
- })
23
-
24
- ActiveRecord::Schema.define(:version => 0) do
25
- create_table :users do |t|
26
- t.string :username
27
- end
28
- end