active_record-events 4.0.1 → 4.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 18d2fc6a8788d3190b5da3dd8dc45fbf307bb55344a6072ba24db67ff9b0e6cb
4
- data.tar.gz: 17767f7e00810c9717cc5fa4bd92d49303b22427eb5da06a3f27ea39f8a4fbf8
3
+ metadata.gz: 7ec4cd90a44c6c550a55b4fed0e50686380441cb86a4089a9716d8d5db350984
4
+ data.tar.gz: bf03cc5c153173f2bf842d240374e00e7a94957fcdd4b855dab0a00fc4999a12
5
5
  SHA512:
6
- metadata.gz: d39a25b8c5695aa8c81b9780744f0639ab2b0bac74296486721ec1c28cad0c67f801441b78c3faa48162d60938297a33638dada9e597a84a7c8c888c94cc86b2
7
- data.tar.gz: afb5395aba7821776ff9aa3790d0f04f625daa1a4de6495e9069cf210a4ea3023b7449bb6cce58e9eb158887a541da6f927b9ba03e61df0974b6cd473a6beacf
6
+ metadata.gz: 7c567f576431e641cd39dad28b71c033093b8cd51e368003ec93b1a44ff1177ac0272a257d1b73b2398072aeb4d962d568865bbd110e2116085d76b513c5da3a
7
+ data.tar.gz: 1b83157999bf3ad63a76475fcf4b011f0f95cd19d83792f1db2a9f5eaedcca178a69fcfccb01898f7d9452ed06d2ba1c28cac3bb0fe657f5e6ed7304b2db4dd2
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # ActiveRecord::Events [![Gem version](https://img.shields.io/gem/v/active_record-events)](https://rubygems.org/gems/active_record-events) [![Build status](https://img.shields.io/travis/pienkowb/active_record-events/develop)](https://travis-ci.org/pienkowb/active_record-events) [![Coverage status](https://img.shields.io/coveralls/github/pienkowb/active_record-events/develop)](https://coveralls.io/github/pienkowb/active_record-events) [![Maintainability status](https://img.shields.io/codeclimate/maintainability/pienkowb/active_record-events)](https://codeclimate.com/github/pienkowb/active_record-events)
1
+ # ActiveRecord::Events [![Gem version](https://img.shields.io/gem/v/active_record-events)](https://rubygems.org/gems/active_record-events) [![Build status](https://img.shields.io/github/actions/workflow/status/pienkowb/active_record-events/test.yml?branch=develop)](https://github.com/pienkowb/active_record-events/actions/workflows/test.yml?query=branch%3Adevelop) [![Coverage status](https://img.shields.io/coveralls/github/pienkowb/active_record-events/develop)](https://coveralls.io/github/pienkowb/active_record-events) [![Maintainability status](https://img.shields.io/codeclimate/maintainability/pienkowb/active_record-events)](https://codeclimate.com/github/pienkowb/active_record-events)
2
2
 
3
3
  An ActiveRecord extension providing convenience methods for timestamp management.
4
4
 
@@ -119,6 +119,26 @@ has_event :complete, field_name: :completion_time
119
119
 
120
120
  Note that the `field_name` option takes precedence over the `field_type` option.
121
121
 
122
+ ### Comparison strategy
123
+
124
+ By default the timestamp's presence will dictate the behavior. However in some cases you may want to check against the current time.
125
+
126
+ You can do this with the `strategy` option, which can be either `presence` or `time_comparison`:
127
+
128
+ ```ruby
129
+ has_event :complete, strategy: :time_comparison
130
+ ```
131
+
132
+ **Example:**
133
+
134
+ ```ruby
135
+ task.completed_at = 1.hour.ago
136
+ task.completed? # => true
137
+
138
+ task.completed_at = 1.hour.from_now
139
+ task.completed? # => false
140
+ ```
141
+
122
142
  ### Specifying an object
123
143
 
124
144
  There are events which do not relate to a model itself but to one of its attributes – take the `User` model with the `email_confirmed_at` field as an example.
@@ -208,6 +228,8 @@ end
208
228
  - [Tomasz Skupiński](https://github.com/tskupinski)
209
229
  - [Oskar Janusz](https://github.com/oskaror)
210
230
  - [Mike Rogers](https://github.com/MikeRogers0)
231
+ - [Spencer Rogers](https://github.com/serogers)
232
+ - [Benno Bielmeier](https://github.com/bbenno)
211
233
 
212
234
  ## See also
213
235
 
@@ -6,11 +6,13 @@ module ActiveRecord
6
6
  def initialize(event_name, options)
7
7
  @options = options
8
8
  @naming = Naming.new(event_name, options)
9
+ @strategy = options[:strategy].try(:to_sym)
10
+ @field_type = options[:field_type].try(:to_sym)
9
11
  end
10
12
 
11
13
  def instance_methods
12
14
  Module.new.tap do |module_|
13
- define_predicate_method(module_, naming)
15
+ define_predicate_method(module_, naming, strategy)
14
16
  define_inverse_predicate_method(module_, naming)
15
17
 
16
18
  define_action_method(module_, naming)
@@ -20,11 +22,13 @@ module ActiveRecord
20
22
 
21
23
  def class_methods
22
24
  Module.new.tap do |module_|
23
- define_collective_action_method(module_, naming)
25
+ time_class = field_type == :date ? Date : Time
26
+
27
+ define_collective_action_method(module_, naming, time_class)
24
28
 
25
29
  unless options[:skip_scopes]
26
- define_scope_method(module_, naming)
27
- define_inverse_scope_method(module_, naming)
30
+ define_scope_method(module_, naming, strategy, time_class)
31
+ define_inverse_scope_method(module_, naming, strategy, time_class)
28
32
  end
29
33
  end
30
34
  end
@@ -33,10 +37,16 @@ module ActiveRecord
33
37
 
34
38
  attr_reader :options
35
39
  attr_reader :naming
40
+ attr_reader :strategy
41
+ attr_reader :field_type
36
42
 
37
- def define_predicate_method(module_, naming)
43
+ def define_predicate_method(module_, naming, strategy)
38
44
  module_.send(:define_method, naming.predicate) do
39
- self[naming.field].present?
45
+ if strategy == :time_comparison
46
+ self[naming.field].present? && !self[naming.field].future?
47
+ else
48
+ self[naming.field].present?
49
+ end
40
50
  end
41
51
  end
42
52
 
@@ -62,25 +72,35 @@ module ActiveRecord
62
72
  end
63
73
  end
64
74
 
65
- def define_collective_action_method(module_, naming)
75
+ def define_collective_action_method(module_, naming, time_class)
66
76
  module_.send(:define_method, naming.collective_action) do
67
77
  if respond_to?(:touch_all)
68
78
  touch_all(naming.field)
69
79
  else
70
- update_all(naming.field => Time.current)
80
+ update_all(naming.field => time_class.current)
71
81
  end
72
82
  end
73
83
  end
74
84
 
75
- def define_scope_method(module_, naming)
85
+ def define_scope_method(module_, naming, strategy, time_class)
76
86
  module_.send(:define_method, naming.scope) do
77
- where(arel_table[naming.field].not_eq(nil))
87
+ if strategy == :time_comparison
88
+ where(arel_table[naming.field].lteq(time_class.current))
89
+ else
90
+ where(arel_table[naming.field].not_eq(nil))
91
+ end
78
92
  end
79
93
  end
80
94
 
81
- def define_inverse_scope_method(module_, naming)
95
+ def define_inverse_scope_method(module_, naming, strategy, time_class)
82
96
  module_.send(:define_method, naming.inverse_scope) do
83
- where(arel_table[naming.field].eq(nil))
97
+ arel_field = arel_table[naming.field]
98
+
99
+ if strategy == :time_comparison
100
+ where(arel_field.eq(nil).or(arel_field.gt(time_class.current)))
101
+ else
102
+ where(arel_field.eq(nil))
103
+ end
84
104
  end
85
105
  end
86
106
  end
@@ -1,5 +1,5 @@
1
1
  module ActiveRecord
2
2
  module Events
3
- VERSION = '4.0.1'.freeze
3
+ VERSION = '4.1.1'.freeze
4
4
  end
5
5
  end
@@ -6,7 +6,7 @@ require 'active_record/events/macro'
6
6
  module ActiveRecord
7
7
  module Generators
8
8
  class EventGenerator < Rails::Generators::Base
9
- MACRO_OPTIONS = %w[object field_type skip_scopes].freeze
9
+ MACRO_OPTIONS = %w[object field_type skip_scopes strategy].freeze
10
10
 
11
11
  argument :model_name, type: :string
12
12
  argument :event_name, type: :string
@@ -17,6 +17,8 @@ module ActiveRecord
17
17
  desc: 'The field type (datetime or date)'
18
18
  class_option :object, type: :string,
19
19
  desc: 'The name of the object'
20
+ class_option :strategy, type: :string,
21
+ desc: 'The comparison strategy (presence or time_comparison)'
20
22
 
21
23
  source_root File.expand_path('templates', __dir__)
22
24
 
@@ -38,8 +40,9 @@ module ActiveRecord
38
40
 
39
41
  macro_options = options.slice(*MACRO_OPTIONS)
40
42
  macro = ActiveRecord::Events::Macro.new(event_name, macro_options)
43
+ pattern = /^\s*class\s.+\n/
41
44
 
42
- inject_into_file model_file_path, "\s\s#{macro}\n", after: /class.+\n/
45
+ inject_into_file model_file_path, "\s\s#{macro}\n", after: pattern
43
46
  end
44
47
 
45
48
  private
@@ -71,4 +71,108 @@ RSpec.describe ActiveRecord::Events do
71
71
 
72
72
  expect(task.reload).to be_completed
73
73
  end
74
+
75
+ describe 'time comparison strategy' do
76
+ it 'defines a predicate method comparing against current time' do
77
+ task.update!(expired_at: 1.hour.from_now)
78
+ expect(task.expired?).to eq(false)
79
+
80
+ task.update!(expired_at: 1.hour.ago)
81
+ expect(task.expired?).to eq(true)
82
+
83
+ task.update!(expired_at: nil)
84
+ expect(task.expired?).to eq(false)
85
+ end
86
+
87
+ it 'defines an inverse predicate method comparing against current time' do
88
+ task.update!(expired_at: 1.hour.from_now)
89
+ expect(task.not_expired?).to eq(true)
90
+
91
+ task.update!(expired_at: 1.hour.ago)
92
+ expect(task.not_expired?).to eq(false)
93
+
94
+ task.update!(expired_at: nil)
95
+ expect(task.not_expired?).to eq(true)
96
+ end
97
+
98
+ it 'defines a scope comparing against current time' do
99
+ task.update!(expired_at: 1.hour.from_now)
100
+ expect(Task.expired).not_to include(task)
101
+
102
+ task.update!(expired_at: 1.hour.ago)
103
+ expect(Task.expired).to include(task)
104
+
105
+ task.update!(expired_at: nil)
106
+ expect(Task.expired).not_to include(task)
107
+ end
108
+
109
+ it 'defines an inverse scope comparing against current time' do
110
+ task.update!(expired_at: 1.hour.from_now)
111
+ expect(Task.not_expired).to include(task)
112
+
113
+ task.update!(expired_at: 1.hour.ago)
114
+ expect(Task.not_expired).not_to include(task)
115
+
116
+ task.update!(expired_at: nil)
117
+ expect(Task.not_expired).to include(task)
118
+ end
119
+
120
+ context 'with a date field' do
121
+ it 'defines a predicate method comparing against current date' do
122
+ task.update!(notified_on: Date.yesterday)
123
+ expect(task.notified?).to eq(true)
124
+
125
+ task.update!(notified_on: Date.today)
126
+ expect(task.notified?).to eq(true)
127
+
128
+ task.update!(notified_on: Date.tomorrow)
129
+ expect(task.notified?).to eq(false)
130
+
131
+ task.update!(notified_on: nil)
132
+ expect(task.notified?).to eq(false)
133
+ end
134
+
135
+ it 'defines an inverse predicate method comparing against current date' do
136
+ task.update!(notified_on: Date.yesterday)
137
+ expect(task.not_notified?).to eq(false)
138
+
139
+ task.update!(notified_on: Date.today)
140
+ expect(task.not_notified?).to eq(false)
141
+
142
+ task.update!(notified_on: Date.tomorrow)
143
+ expect(task.not_notified?).to eq(true)
144
+
145
+ task.update!(notified_on: nil)
146
+ expect(task.not_notified?).to eq(true)
147
+ end
148
+
149
+ it 'defines a scope comparing against current date' do
150
+ task.update!(notified_on: Date.yesterday)
151
+ expect(Task.notified).to include(task)
152
+
153
+ task.update!(notified_on: Date.today)
154
+ expect(Task.notified).to include(task)
155
+
156
+ task.update!(notified_on: Date.tomorrow)
157
+ expect(Task.notified).not_to include(task)
158
+
159
+ task.update!(notified_on: nil)
160
+ expect(Task.notified).not_to include(task)
161
+ end
162
+
163
+ it 'defines an inverse scope comparing against current date' do
164
+ task.update!(notified_on: Date.yesterday)
165
+ expect(Task.not_notified).not_to include(task)
166
+
167
+ task.update!(notified_on: Date.today)
168
+ expect(Task.not_notified).not_to include(task)
169
+
170
+ task.update!(notified_on: Date.tomorrow)
171
+ expect(Task.not_notified).to include(task)
172
+
173
+ task.update!(notified_on: nil)
174
+ expect(Task.not_notified).to include(task)
175
+ end
176
+ end
177
+ end
74
178
  end
@@ -1,6 +1,8 @@
1
1
  class Task < ActiveRecord::Base
2
2
  has_event :complete
3
- has_events :archive, skip_scopes: true
3
+ has_event :archive, skip_scopes: true
4
+ has_event :expire, strategy: :time_comparison
5
+ has_event :notify, field_type: :date, strategy: :time_comparison
4
6
 
5
7
  def complete!
6
8
  super
@@ -0,0 +1,5 @@
1
+ class AddExpiredAtToTasks < ACTIVE_RECORD_MIGRATION_CLASS
2
+ def change
3
+ add_column :tasks, :expired_at, :datetime
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class AddNotifiedOnToTasks < ACTIVE_RECORD_MIGRATION_CLASS
2
+ def change
3
+ add_column :tasks, :notified_on, :date
4
+ end
5
+ end
@@ -2,20 +2,22 @@
2
2
  # of editing this file, please use the migrations feature of Active Record to
3
3
  # incrementally modify your database, and then regenerate this schema definition.
4
4
  #
5
- # Note that this schema.rb definition is the authoritative source for your
6
- # database schema. If you need to create the application database on another
7
- # system, you should be using db:schema:load, not running all the migrations
8
- # from scratch. The latter is a flawed and unsustainable approach (the more migrations
9
- # you'll amass, the slower it'll run and the greater likelihood for issues).
5
+ # This file is the source Rails uses to define your schema when running `bin/rails
6
+ # db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
7
+ # be faster and is potentially less error prone than running all of your
8
+ # migrations from scratch. Old migrations may fail to apply correctly if those
9
+ # migrations use external dependencies or application code.
10
10
  #
11
11
  # It's strongly recommended that you check this file into your version control system.
12
12
 
13
- ActiveRecord::Schema.define(version: 2015_08_13_132804) do
13
+ ActiveRecord::Schema.define(version: 2023_03_08_160359) do
14
14
 
15
15
  create_table "tasks", force: :cascade do |t|
16
16
  t.datetime "completed_at"
17
17
  t.datetime "created_at", null: false
18
18
  t.datetime "updated_at", null: false
19
+ t.datetime "expired_at"
20
+ t.date "notified_on"
19
21
  end
20
22
 
21
23
  end
@@ -2,7 +2,13 @@ require 'spec_helper'
2
2
  require 'generators/active_record/event/event_generator'
3
3
 
4
4
  RSpec.describe ActiveRecord::Generators::EventGenerator, type: :generator do
5
- arguments %w[task complete --field-type=date --skip-scopes]
5
+ arguments %w[
6
+ task complete
7
+ --field-type=date
8
+ --skip-scopes
9
+ --strategy=time_comparison
10
+ ]
11
+
6
12
  destination File.expand_path('../../../../tmp', __dir__)
7
13
 
8
14
  before { prepare_destination }
@@ -30,7 +36,7 @@ RSpec.describe ActiveRecord::Generators::EventGenerator, type: :generator do
30
36
 
31
37
  assert_file 'app/models/task.rb', <<-RUBY.strip_heredoc
32
38
  class Task < ActiveRecord::Base
33
- has_event :complete, field_type: :date, skip_scopes: true
39
+ has_event :complete, field_type: :date, skip_scopes: true, strategy: :time_comparison
34
40
  end
35
41
  RUBY
36
42
  end
data/spec/spec_helper.rb CHANGED
@@ -1,9 +1,20 @@
1
1
  ENV['RAILS_ENV'] ||= 'test'
2
2
 
3
3
  require 'bundler/setup'
4
- require 'coveralls'
4
+ require 'simplecov'
5
5
 
6
- Coveralls.wear!
6
+ SimpleCov.start do
7
+ if ENV['CI']
8
+ require 'simplecov-lcov'
9
+
10
+ SimpleCov::Formatter::LcovFormatter.config do |config|
11
+ config.report_with_single_file = true
12
+ config.single_report_path = 'coverage/lcov.info'
13
+ end
14
+
15
+ formatter SimpleCov::Formatter::LcovFormatter
16
+ end
17
+ end
7
18
 
8
19
  require File.expand_path('dummy/config/environment.rb', __dir__)
9
20
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_record-events
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.1
4
+ version: 4.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bartosz Pieńkowski
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-03-15 00:00:00.000000000 Z
11
+ date: 2023-03-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -52,20 +52,6 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '2.2'
55
- - !ruby/object:Gem::Dependency
56
- name: coveralls
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - "~>"
60
- - !ruby/object:Gem::Version
61
- version: '0.8'
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - "~>"
67
- - !ruby/object:Gem::Version
68
- version: '0.8'
69
55
  - !ruby/object:Gem::Dependency
70
56
  name: factory_girl
71
57
  requirement: !ruby/object:Gem::Requirement
@@ -122,6 +108,34 @@ dependencies:
122
108
  - - "~>"
123
109
  - !ruby/object:Gem::Version
124
110
  version: 0.50.0
111
+ - !ruby/object:Gem::Dependency
112
+ name: simplecov
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 0.16.1
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: 0.16.1
125
+ - !ruby/object:Gem::Dependency
126
+ name: simplecov-lcov
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: 0.7.0
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: 0.7.0
125
139
  - !ruby/object:Gem::Dependency
126
140
  name: sqlite3
127
141
  requirement: !ruby/object:Gem::Requirement
@@ -191,6 +205,8 @@ files:
191
205
  - spec/dummy/config/database.yml
192
206
  - spec/dummy/config/environment.rb
193
207
  - spec/dummy/db/migrate/20150813132804_create_tasks.rb
208
+ - spec/dummy/db/migrate/20220609015338_add_expired_at_to_tasks.rb
209
+ - spec/dummy/db/migrate/20230308160359_add_notified_on_to_tasks.rb
194
210
  - spec/dummy/db/schema.rb
195
211
  - spec/generators/active_record/event/event_generator_spec.rb
196
212
  - spec/spec_helper.rb
@@ -216,7 +232,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
216
232
  - !ruby/object:Gem::Version
217
233
  version: '0'
218
234
  requirements: []
219
- rubygems_version: 3.0.3
235
+ rubygems_version: 3.1.6
220
236
  signing_key:
221
237
  specification_version: 4
222
238
  summary: Manage timestamps in ActiveRecord models
@@ -228,6 +244,8 @@ test_files:
228
244
  - spec/dummy/config/boot.rb
229
245
  - spec/dummy/db/schema.rb
230
246
  - spec/dummy/db/migrate/20150813132804_create_tasks.rb
247
+ - spec/dummy/db/migrate/20220609015338_add_expired_at_to_tasks.rb
248
+ - spec/dummy/db/migrate/20230308160359_add_notified_on_to_tasks.rb
231
249
  - spec/active_record/events/method_factory_spec.rb
232
250
  - spec/active_record/events/naming_spec.rb
233
251
  - spec/active_record/events/macro_spec.rb