active_record-events 4.0.1 → 4.1.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
2
  SHA256:
3
- metadata.gz: 18d2fc6a8788d3190b5da3dd8dc45fbf307bb55344a6072ba24db67ff9b0e6cb
4
- data.tar.gz: 17767f7e00810c9717cc5fa4bd92d49303b22427eb5da06a3f27ea39f8a4fbf8
3
+ metadata.gz: 1eace6bdc4fb78e2ac6c90a8521c976f8b707caeed3e035da39d0218e0724ad7
4
+ data.tar.gz: 8e9754766994b37d29dd29a9a44963fa9c90b1d4f90418e8e06bf3863f32ea02
5
5
  SHA512:
6
- metadata.gz: d39a25b8c5695aa8c81b9780744f0639ab2b0bac74296486721ec1c28cad0c67f801441b78c3faa48162d60938297a33638dada9e597a84a7c8c888c94cc86b2
7
- data.tar.gz: afb5395aba7821776ff9aa3790d0f04f625daa1a4de6495e9069cf210a4ea3023b7449bb6cce58e9eb158887a541da6f927b9ba03e61df0974b6cd473a6beacf
6
+ metadata.gz: 1260729b2209565867c7824850be4450b45565da6fc1d7864d247ff6fcc659b89f1969ef2f9530e2bfc7d8de934a90f3eab585c18fb2c0d8ec3809f8242a864f
7
+ data.tar.gz: b876227c57657edb00198bda46eb42e2b2aebdb39379cd61261c9586805a5e736575515b2ddbbd336ed7e1612b603b0f6c30982b3c8e721da9ef849a93f41a2f
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/workflow/status/pienkowb/active_record-events/Test/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,7 @@ 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)
211
232
 
212
233
  ## See also
213
234
 
@@ -6,11 +6,12 @@ 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)
9
10
  end
10
11
 
11
12
  def instance_methods
12
13
  Module.new.tap do |module_|
13
- define_predicate_method(module_, naming)
14
+ define_predicate_method(module_, naming, strategy)
14
15
  define_inverse_predicate_method(module_, naming)
15
16
 
16
17
  define_action_method(module_, naming)
@@ -23,8 +24,8 @@ module ActiveRecord
23
24
  define_collective_action_method(module_, naming)
24
25
 
25
26
  unless options[:skip_scopes]
26
- define_scope_method(module_, naming)
27
- define_inverse_scope_method(module_, naming)
27
+ define_scope_method(module_, naming, strategy)
28
+ define_inverse_scope_method(module_, naming, strategy)
28
29
  end
29
30
  end
30
31
  end
@@ -33,10 +34,15 @@ module ActiveRecord
33
34
 
34
35
  attr_reader :options
35
36
  attr_reader :naming
37
+ attr_reader :strategy
36
38
 
37
- def define_predicate_method(module_, naming)
39
+ def define_predicate_method(module_, naming, strategy)
38
40
  module_.send(:define_method, naming.predicate) do
39
- self[naming.field].present?
41
+ if strategy == :time_comparison
42
+ self[naming.field].present? && self[naming.field].past?
43
+ else
44
+ self[naming.field].present?
45
+ end
40
46
  end
41
47
  end
42
48
 
@@ -72,15 +78,25 @@ module ActiveRecord
72
78
  end
73
79
  end
74
80
 
75
- def define_scope_method(module_, naming)
81
+ def define_scope_method(module_, naming, strategy)
76
82
  module_.send(:define_method, naming.scope) do
77
- where(arel_table[naming.field].not_eq(nil))
83
+ if strategy == :time_comparison
84
+ where(arel_table[naming.field].lteq(Time.current))
85
+ else
86
+ where(arel_table[naming.field].not_eq(nil))
87
+ end
78
88
  end
79
89
  end
80
90
 
81
- def define_inverse_scope_method(module_, naming)
91
+ def define_inverse_scope_method(module_, naming, strategy)
82
92
  module_.send(:define_method, naming.inverse_scope) do
83
- where(arel_table[naming.field].eq(nil))
93
+ arel_field = arel_table[naming.field]
94
+
95
+ if strategy == :time_comparison
96
+ where(arel_field.eq(nil).or(arel_field.gt(Time.current)))
97
+ else
98
+ where(arel_field.eq(nil))
99
+ end
84
100
  end
85
101
  end
86
102
  end
@@ -1,5 +1,5 @@
1
1
  module ActiveRecord
2
2
  module Events
3
- VERSION = '4.0.1'.freeze
3
+ VERSION = '4.1.0'.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,50 @@ 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_columns(expired_at: 1.hour.from_now)
78
+ expect(task.expired?).to eq(false)
79
+
80
+ task.update_columns(expired_at: 1.hour.ago)
81
+ expect(task.expired?).to eq(true)
82
+
83
+ task.update_columns(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_columns(expired_at: 1.hour.from_now)
89
+ expect(task.not_expired?).to eq(true)
90
+
91
+ task.update_columns(expired_at: 1.hour.ago)
92
+ expect(task.not_expired?).to eq(false)
93
+
94
+ task.update_columns(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_columns(expired_at: 1.hour.from_now)
100
+ expect(Task.expired).not_to include(task)
101
+
102
+ task.update_columns(expired_at: 1.hour.ago)
103
+ expect(Task.expired).to include(task)
104
+
105
+ task.update_columns(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_columns(expired_at: 1.hour.from_now)
111
+ expect(Task.not_expired).to include(task)
112
+
113
+ task.update_columns(expired_at: 1.hour.ago)
114
+ expect(Task.not_expired).not_to include(task)
115
+
116
+ task.update_columns(expired_at: nil)
117
+ expect(Task.not_expired).to include(task)
118
+ end
119
+ end
74
120
  end
@@ -1,6 +1,7 @@
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_events :expire, strategy: :time_comparison
4
5
 
5
6
  def complete!
6
7
  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
@@ -2,20 +2,21 @@
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: 2022_06_09_015338) 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"
19
20
  end
20
21
 
21
22
  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.0
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: 2022-06-11 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,7 @@ 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
194
209
  - spec/dummy/db/schema.rb
195
210
  - spec/generators/active_record/event/event_generator_spec.rb
196
211
  - spec/spec_helper.rb
@@ -216,7 +231,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
216
231
  - !ruby/object:Gem::Version
217
232
  version: '0'
218
233
  requirements: []
219
- rubygems_version: 3.0.3
234
+ rubygems_version: 3.1.6
220
235
  signing_key:
221
236
  specification_version: 4
222
237
  summary: Manage timestamps in ActiveRecord models
@@ -228,6 +243,7 @@ test_files:
228
243
  - spec/dummy/config/boot.rb
229
244
  - spec/dummy/db/schema.rb
230
245
  - spec/dummy/db/migrate/20150813132804_create_tasks.rb
246
+ - spec/dummy/db/migrate/20220609015338_add_expired_at_to_tasks.rb
231
247
  - spec/active_record/events/method_factory_spec.rb
232
248
  - spec/active_record/events/naming_spec.rb
233
249
  - spec/active_record/events/macro_spec.rb