signal 1.1.0 → 1.2.0

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