phillumeny 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/phillumeny.rb +1 -0
- data/lib/phillumeny/active_model.rb +3 -198
- data/lib/phillumeny/active_model/validate_presence_of_any.rb +202 -0
- data/lib/phillumeny/active_record.rb +35 -0
- data/lib/phillumeny/active_record/cover_query_with_indexes.rb +77 -0
- data/lib/phillumeny/active_record/have_default_value_of.rb +98 -0
- data/lib/phillumeny/version.rb +1 -1
- metadata +7 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cc29628456bbe7ab061f91f3a0519fee437a3351151c66b83b37a93bd5c40573
|
4
|
+
data.tar.gz: 3c1e8512e573d04b100ba7b37fa2e96fb310f29aa47041c442a7ab9d04720d8c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f7a5bb55690d759ebdfb51b7d324621347cfbac9bdd08d58db15b3a5d318d9b87a7d2fba8477d6d6fd97c9a138762fe84361aea8a7b15472c0c2d758cf021de9
|
7
|
+
data.tar.gz: 65cb57ad917ce2a1132335d966d1b88b1a624c34fc610e051230af77d42e595e499b221d7b8606ae0063f377c5eb2156b12e3dd6983a4f0a8697c44964c89615
|
data/lib/phillumeny.rb
CHANGED
@@ -1,203 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Phillumeny
|
4
|
-
|
5
|
-
# Matchers testing ActiveModel functionality
|
6
|
-
module ActiveModel
|
7
|
-
|
8
|
-
def validate_presence_of_any(*args)
|
9
|
-
ValidatePresenceOfAny.new(args)
|
10
|
-
end
|
11
|
-
|
12
|
-
# Used for testing conditional validation of presence where you need one of the other
|
13
|
-
#
|
14
|
-
# @example
|
15
|
-
#
|
16
|
-
# class Webpage
|
17
|
-
#
|
18
|
-
# include ActiveModel::Validations
|
19
|
-
#
|
20
|
-
# attr_accessor :body
|
21
|
-
# attr_accessor :headline
|
22
|
-
# attr_accessor :title
|
23
|
-
#
|
24
|
-
# validates :body,
|
25
|
-
# presence: true
|
26
|
-
#
|
27
|
-
# validates :headline,
|
28
|
-
# presence: true,
|
29
|
-
# unless: -> { title.present? }
|
30
|
-
#
|
31
|
-
# validates :title,
|
32
|
-
# presence: true,
|
33
|
-
# inclusion: { allow_blank: true, in: ['this'] },
|
34
|
-
# unless: -> { headline.present? && headline != 'needs title' }
|
35
|
-
#
|
36
|
-
# end
|
37
|
-
#
|
38
|
-
# RSpec.describe Webpage, type: :model do
|
39
|
-
#
|
40
|
-
# it { should validate_presence_of_any(:headline, :title).valid_values(title: 'this') }
|
41
|
-
# it { should_not validate_presence_of_any(:headline, :title) }
|
42
|
-
# it { should_not validate_presence_of_any(:headline, :title).valid_value(:title, 'that') }
|
43
|
-
#
|
44
|
-
# end
|
45
|
-
class ValidatePresenceOfAny
|
46
|
-
|
47
|
-
attr_reader :attributes
|
48
|
-
attr_reader :subject
|
49
|
-
|
50
|
-
# Description used when matcher is used
|
51
|
-
#
|
52
|
-
# @api private
|
53
|
-
#
|
54
|
-
# @return [String]
|
55
|
-
def description
|
56
|
-
"validate the presence of any of these attributes: #{attributes.join(', ')}"
|
57
|
-
end
|
58
|
-
|
59
|
-
# Compiles the error message for display
|
60
|
-
#
|
61
|
-
# @api private
|
62
|
-
#
|
63
|
-
# @return [String]
|
64
|
-
def failure_message
|
65
|
-
messages = [subject.inspect]
|
66
|
-
attributes.each do |attribute|
|
67
|
-
messages << subject.errors.full_messages_for(attribute)
|
68
|
-
end
|
69
|
-
messages.compact.join("\n")
|
70
|
-
end
|
71
|
-
|
72
|
-
# Sets up arguments for the matcher
|
73
|
-
#
|
74
|
-
# @api private
|
75
|
-
#
|
76
|
-
# @return [void]
|
77
|
-
def initialize(args)
|
78
|
-
@attributes = args
|
79
|
-
end
|
80
|
-
|
81
|
-
# Runs the logic to determine if expectations are being met
|
82
|
-
#
|
83
|
-
# @api private
|
84
|
-
#
|
85
|
-
# @return [Boolean]
|
86
|
-
def matches?(subject)
|
87
|
-
@subject = subject
|
88
|
-
store_initial_values
|
89
|
-
invalid_when_none_present? && attributes.all? do |attribute|
|
90
|
-
clear_attributes_and_errors
|
91
|
-
initialize_value(attribute)
|
92
|
-
free_of_errors_on_attribute?(attribute) ? other_attributes_valid?(attribute) : false
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
# Explicitly set the value to test for an attribute if 'X' is not acceptable
|
97
|
-
#
|
98
|
-
# @api public
|
99
|
-
#
|
100
|
-
# @example
|
101
|
-
# it { should validate_presence_of_any(:headline, :title).valid_value(:title, 'A valid title') }
|
102
|
-
#
|
103
|
-
# @return [self]
|
104
|
-
def valid_value(attribute, value)
|
105
|
-
attribute_values[attribute] = value
|
106
|
-
self
|
107
|
-
end
|
108
|
-
|
109
|
-
# Explicitly set the value to test for an attribute if 'X' is not acceptable
|
110
|
-
#
|
111
|
-
# @api public
|
112
|
-
#
|
113
|
-
# @example
|
114
|
-
# it do
|
115
|
-
# should validate_presence_of_any(:headline, :title).valid_values(title: 'A valid title')
|
116
|
-
# end
|
117
|
-
#
|
118
|
-
# @return [self]
|
119
|
-
def valid_values(**values)
|
120
|
-
attribute_values.merge!(values)
|
121
|
-
self
|
122
|
-
end
|
123
|
-
|
124
|
-
private
|
125
|
-
|
126
|
-
# Storage for our initial and valid values for the attributes we are testing against
|
127
|
-
#
|
128
|
-
# @api private
|
129
|
-
#
|
130
|
-
# @return [Hash]
|
131
|
-
def attribute_values
|
132
|
-
@attribute_values ||= {}
|
133
|
-
end
|
134
|
-
|
135
|
-
# Clears all of the attributes that we are testing against
|
136
|
-
#
|
137
|
-
# @api private
|
138
|
-
#
|
139
|
-
# @return [void]
|
140
|
-
def clear_attributes_and_errors
|
141
|
-
attributes.each do |attribute|
|
142
|
-
subject.send("#{attribute}=", nil)
|
143
|
-
end
|
144
|
-
subject.errors.clear
|
145
|
-
end
|
146
|
-
|
147
|
-
# Checks that the current attribute we are checking against passes its other validations
|
148
|
-
#
|
149
|
-
# @api private
|
150
|
-
#
|
151
|
-
# @return [Boolean]
|
152
|
-
def free_of_errors_on_attribute?(attribute)
|
153
|
-
subject.class.validators_on(attribute).each do |validator|
|
154
|
-
# Can/do we use self[attribute] here or would that be better used in the
|
155
|
-
# conditional Proc on the validation itself?
|
156
|
-
validator.validate_each(subject, attribute, subject.send(attribute))
|
157
|
-
end
|
158
|
-
subject.errors[attribute].empty?
|
159
|
-
end
|
160
|
-
|
161
|
-
# Sets the value of the attribute that we currently testing against
|
162
|
-
#
|
163
|
-
# @api private
|
164
|
-
#
|
165
|
-
# @return [void]
|
166
|
-
def initialize_value(attribute)
|
167
|
-
subject.send("#{attribute}=", attribute_values[attribute] || 'X')
|
168
|
-
end
|
169
|
-
|
170
|
-
def invalid_when_none_present?
|
171
|
-
clear_attributes_and_errors
|
172
|
-
!subject.valid? && attributes.all? { |attribute| subject.errors[attribute].present? }
|
173
|
-
end
|
174
|
-
|
175
|
-
# Checks that the other attributes that we are checking against are valid as promised
|
176
|
-
#
|
177
|
-
# @api private
|
178
|
-
#
|
179
|
-
# @return [Boolean]
|
180
|
-
def other_attributes_valid?(exclude_attribute)
|
181
|
-
subject.valid?
|
182
|
-
(attributes - [exclude_attribute]).all? do |attribute|
|
183
|
-
subject.errors[attribute].empty?
|
184
|
-
end
|
185
|
-
end
|
186
|
-
|
187
|
-
# Store the initial values of our subject before we start messing around with it
|
188
|
-
#
|
189
|
-
# @api private
|
190
|
-
#
|
191
|
-
# @return [void]
|
192
|
-
def store_initial_values
|
193
|
-
attributes.each do |attribute|
|
194
|
-
next if attribute_values.key?(attribute)
|
195
|
-
attribute_values[attribute] = subject.send(attribute)
|
196
|
-
end
|
197
|
-
end
|
198
|
-
|
199
|
-
end
|
200
|
-
|
4
|
+
module ActiveModel # :nodoc:
|
201
5
|
end
|
202
|
-
|
203
6
|
end
|
7
|
+
|
8
|
+
require 'phillumeny/active_model/validate_presence_of_any'
|
@@ -0,0 +1,202 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phillumeny
|
4
|
+
|
5
|
+
module ActiveModel # :nodoc:
|
6
|
+
|
7
|
+
def validate_presence_of_any(*args)
|
8
|
+
ValidatePresenceOfAny.new(args)
|
9
|
+
end
|
10
|
+
|
11
|
+
# Used for testing conditional validation of presence where you need one of the other
|
12
|
+
#
|
13
|
+
# @example
|
14
|
+
#
|
15
|
+
# class Webpage
|
16
|
+
#
|
17
|
+
# include ActiveModel::Validations
|
18
|
+
#
|
19
|
+
# attr_accessor :body
|
20
|
+
# attr_accessor :headline
|
21
|
+
# attr_accessor :title
|
22
|
+
#
|
23
|
+
# validates :body,
|
24
|
+
# presence: true
|
25
|
+
#
|
26
|
+
# validates :headline,
|
27
|
+
# presence: true,
|
28
|
+
# unless: -> { title.present? }
|
29
|
+
#
|
30
|
+
# validates :title,
|
31
|
+
# presence: true,
|
32
|
+
# inclusion: { allow_blank: true, in: ['this'] },
|
33
|
+
# unless: -> { headline.present? && headline != 'needs title' }
|
34
|
+
#
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# RSpec.describe Webpage, type: :model do
|
38
|
+
#
|
39
|
+
# it { should validate_presence_of_any(:headline, :title).valid_values(title: 'this') }
|
40
|
+
# it { should_not validate_presence_of_any(:headline, :title) }
|
41
|
+
# it { should_not validate_presence_of_any(:headline, :title).valid_value(:title, 'that') }
|
42
|
+
#
|
43
|
+
# end
|
44
|
+
class ValidatePresenceOfAny
|
45
|
+
|
46
|
+
attr_reader :attributes
|
47
|
+
attr_reader :subject
|
48
|
+
|
49
|
+
# Description used when matcher is used
|
50
|
+
#
|
51
|
+
# @api private
|
52
|
+
#
|
53
|
+
# @return [String]
|
54
|
+
def description
|
55
|
+
"validate the presence of any of these attributes: #{attributes.join(', ')}"
|
56
|
+
end
|
57
|
+
|
58
|
+
# Compiles the error message for display
|
59
|
+
#
|
60
|
+
# @api private
|
61
|
+
#
|
62
|
+
# @return [String]
|
63
|
+
def failure_message
|
64
|
+
messages = [subject.inspect]
|
65
|
+
attributes.each do |attribute|
|
66
|
+
messages << subject.errors.full_messages_for(attribute)
|
67
|
+
end
|
68
|
+
messages.compact.join("\n")
|
69
|
+
end
|
70
|
+
|
71
|
+
# Sets up arguments for the matcher
|
72
|
+
#
|
73
|
+
# @api private
|
74
|
+
#
|
75
|
+
# @return [void]
|
76
|
+
def initialize(args)
|
77
|
+
@attributes = args
|
78
|
+
end
|
79
|
+
|
80
|
+
# Runs the logic to determine if expectations are being met
|
81
|
+
#
|
82
|
+
# @api private
|
83
|
+
#
|
84
|
+
# @return [Boolean]
|
85
|
+
def matches?(subject)
|
86
|
+
@subject = subject
|
87
|
+
store_initial_values
|
88
|
+
invalid_when_none_present? && attributes.all? do |attribute|
|
89
|
+
clear_attributes_and_errors
|
90
|
+
initialize_value(attribute)
|
91
|
+
free_of_errors_on_attribute?(attribute) ? other_attributes_valid?(attribute) : false
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Explicitly set the value to test for an attribute if 'X' is not acceptable
|
96
|
+
#
|
97
|
+
# @api public
|
98
|
+
#
|
99
|
+
# @example
|
100
|
+
# it { should validate_presence_of_any(:headline, :title).valid_value(:title, 'A valid title') }
|
101
|
+
#
|
102
|
+
# @return [self]
|
103
|
+
def valid_value(attribute, value)
|
104
|
+
attribute_values[attribute] = value
|
105
|
+
self
|
106
|
+
end
|
107
|
+
|
108
|
+
# Explicitly set the value to test for an attribute if 'X' is not acceptable
|
109
|
+
#
|
110
|
+
# @api public
|
111
|
+
#
|
112
|
+
# @example
|
113
|
+
# it do
|
114
|
+
# should validate_presence_of_any(:headline, :title).valid_values(title: 'A valid title')
|
115
|
+
# end
|
116
|
+
#
|
117
|
+
# @return [self]
|
118
|
+
def valid_values(**values)
|
119
|
+
attribute_values.merge!(values)
|
120
|
+
self
|
121
|
+
end
|
122
|
+
|
123
|
+
private
|
124
|
+
|
125
|
+
# Storage for our initial and valid values for the attributes we are testing against
|
126
|
+
#
|
127
|
+
# @api private
|
128
|
+
#
|
129
|
+
# @return [Hash]
|
130
|
+
def attribute_values
|
131
|
+
@attribute_values ||= {}
|
132
|
+
end
|
133
|
+
|
134
|
+
# Clears all of the attributes that we are testing against
|
135
|
+
#
|
136
|
+
# @api private
|
137
|
+
#
|
138
|
+
# @return [void]
|
139
|
+
def clear_attributes_and_errors
|
140
|
+
attributes.each do |attribute|
|
141
|
+
subject.send("#{attribute}=", nil)
|
142
|
+
end
|
143
|
+
subject.errors.clear
|
144
|
+
end
|
145
|
+
|
146
|
+
# Checks that the current attribute we are checking against passes its other validations
|
147
|
+
#
|
148
|
+
# @api private
|
149
|
+
#
|
150
|
+
# @return [Boolean]
|
151
|
+
def free_of_errors_on_attribute?(attribute)
|
152
|
+
subject.class.validators_on(attribute).each do |validator|
|
153
|
+
# Can/do we use self[attribute] here or would that be better used in the
|
154
|
+
# conditional Proc on the validation itself?
|
155
|
+
validator.validate_each(subject, attribute, subject.send(attribute))
|
156
|
+
end
|
157
|
+
subject.errors[attribute].empty?
|
158
|
+
end
|
159
|
+
|
160
|
+
# Sets the value of the attribute that we currently testing against
|
161
|
+
#
|
162
|
+
# @api private
|
163
|
+
#
|
164
|
+
# @return [void]
|
165
|
+
def initialize_value(attribute)
|
166
|
+
subject.send("#{attribute}=", attribute_values[attribute] || 'X')
|
167
|
+
end
|
168
|
+
|
169
|
+
def invalid_when_none_present?
|
170
|
+
clear_attributes_and_errors
|
171
|
+
!subject.valid? && attributes.all? { |attribute| subject.errors[attribute].present? }
|
172
|
+
end
|
173
|
+
|
174
|
+
# Checks that the other attributes that we are checking against are valid as promised
|
175
|
+
#
|
176
|
+
# @api private
|
177
|
+
#
|
178
|
+
# @return [Boolean]
|
179
|
+
def other_attributes_valid?(exclude_attribute)
|
180
|
+
subject.valid?
|
181
|
+
(attributes - [exclude_attribute]).all? do |attribute|
|
182
|
+
subject.errors[attribute].empty?
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
# Store the initial values of our subject before we start messing around with it
|
187
|
+
#
|
188
|
+
# @api private
|
189
|
+
#
|
190
|
+
# @return [void]
|
191
|
+
def store_initial_values
|
192
|
+
attributes.each do |attribute|
|
193
|
+
next if attribute_values.key?(attribute)
|
194
|
+
attribute_values[attribute] = subject.send(attribute)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
end
|
199
|
+
|
200
|
+
end
|
201
|
+
|
202
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phillumeny
|
4
|
+
module ActiveRecord # :nodoc:
|
5
|
+
|
6
|
+
# Helper methods for working with the database
|
7
|
+
#
|
8
|
+
# All the methods depend on the @subject instance variable being set
|
9
|
+
module TableInformation
|
10
|
+
|
11
|
+
protected
|
12
|
+
|
13
|
+
def model_class
|
14
|
+
@model_class ||= @subject.class
|
15
|
+
end
|
16
|
+
|
17
|
+
# Retrieve all the table
|
18
|
+
def table_columns
|
19
|
+
@table_columns ||= ::ActiveRecord::Base.connection.columns(table_name)
|
20
|
+
end
|
21
|
+
|
22
|
+
def table_indexes
|
23
|
+
@table_indexes ||= ::ActiveRecord::Base.connection.indexes(table_name)
|
24
|
+
end
|
25
|
+
|
26
|
+
def table_name
|
27
|
+
@table_name ||= model_class.table_name
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
require 'phillumeny/active_record/cover_query_with_indexes'
|
35
|
+
require 'phillumeny/active_record/have_default_value_of'
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phillumeny
|
4
|
+
|
5
|
+
module ActiveRecord # :nodoc:
|
6
|
+
|
7
|
+
# Comb through indexes to ensure the columns are covered
|
8
|
+
#
|
9
|
+
# @api public
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
#
|
13
|
+
# [:col1, [:col1, :col2], :col2].each do |columns|
|
14
|
+
# it { should cover_query_with_indexes columns }
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# @return [Phillumeny::ActiveRecord::CoverQueryWithIndexes]
|
18
|
+
def cover_query_with_indexes(columns)
|
19
|
+
CoverQueryWithIndexes.new(columns)
|
20
|
+
end
|
21
|
+
|
22
|
+
# There is often a misunderstanding on how database indexes
|
23
|
+
# work when you are indexing across multiple columns. An index
|
24
|
+
# created for the columns [:col1, :col2] will *usually* cover
|
25
|
+
# where clauses against :col1 and [:col2, :col1] also but does not
|
26
|
+
# guarantee it will be even used if a where clause only has :col2.
|
27
|
+
class CoverQueryWithIndexes
|
28
|
+
|
29
|
+
include Phillumeny::ActiveRecord::TableInformation
|
30
|
+
|
31
|
+
def initialize(columns)
|
32
|
+
self.columns = Array(columns).map(&:to_s)
|
33
|
+
end
|
34
|
+
|
35
|
+
def description
|
36
|
+
"have database indexes that would cover #{columns}"
|
37
|
+
end
|
38
|
+
|
39
|
+
def failure_message
|
40
|
+
return "No database column(s) found for #{invalid_columns.inspect}" unless valid_columns?
|
41
|
+
"The table #{table_name} did not have index to cover queries for #{columns}"
|
42
|
+
end
|
43
|
+
|
44
|
+
def matches?(subject)
|
45
|
+
@subject = subject
|
46
|
+
valid_columns? && matching_index?
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
attr_accessor :columns
|
52
|
+
|
53
|
+
def invalid_columns
|
54
|
+
@invalid_columns ||= columns.reject do |col_name|
|
55
|
+
::ActiveRecord::Base.connection.column_exists?(table_name, col_name)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def matching_index?
|
60
|
+
sized_table_indexs_columns.any? do |relevent_columns|
|
61
|
+
(columns - relevent_columns).empty?
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def sized_table_indexs_columns
|
66
|
+
table_indexes.map { |index| index.columns.first(columns.size) }
|
67
|
+
end
|
68
|
+
|
69
|
+
def valid_columns?
|
70
|
+
invalid_columns.empty?
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phillumeny
|
4
|
+
|
5
|
+
module ActiveRecord # :nodoc:
|
6
|
+
|
7
|
+
# Confirm a default value is getting returned as expected
|
8
|
+
#
|
9
|
+
# @api public
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
#
|
13
|
+
# { user_id: nil, timezone: 'PDT' }.each do |attribute, value|
|
14
|
+
# # Just checks the subject that the right value is present
|
15
|
+
# it { should have_default_value_of(value).for(attribute) }
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# # Checks both the subject and confirms at the database column level
|
19
|
+
# it { should have_default_value_of('PDT').for(:timezone).in_the_database }
|
20
|
+
#
|
21
|
+
# @return [Phillumeny::ActiveRecord::HaveADefaultValueOf]
|
22
|
+
def have_default_value_of(default_value)
|
23
|
+
HaveDefaultValueOf.new(default_value)
|
24
|
+
end
|
25
|
+
|
26
|
+
class HaveDefaultValueOf # :nodoc:
|
27
|
+
# @note
|
28
|
+
# Part of this could live outside of the ActiveRecord module but we have
|
29
|
+
# an option here to confirm and check the database also so leaving
|
30
|
+
# it in here for now
|
31
|
+
|
32
|
+
include Phillumeny::ActiveRecord::TableInformation
|
33
|
+
|
34
|
+
def description
|
35
|
+
"have a default value of '#{default_value}':#{default_value.class} for #{attribute_name}"
|
36
|
+
end
|
37
|
+
|
38
|
+
def failure_message
|
39
|
+
# rubocop:disable LineLength
|
40
|
+
"The value of '#{value_for_attribute}':#{value_for_attribute.class} was found not '#{default_value}':#{default_value.class}"
|
41
|
+
# rubocop:enable LineLength
|
42
|
+
end
|
43
|
+
|
44
|
+
def for(attribute_name)
|
45
|
+
self.attribute_name = attribute_name
|
46
|
+
self
|
47
|
+
end
|
48
|
+
|
49
|
+
def in_the_database
|
50
|
+
self.check_column_default = true
|
51
|
+
self
|
52
|
+
end
|
53
|
+
|
54
|
+
def initialize(default_value)
|
55
|
+
self.default_value = default_value
|
56
|
+
end
|
57
|
+
|
58
|
+
def matches?(subject)
|
59
|
+
@subject = subject
|
60
|
+
default_value_found? && column_configured_for_default_value?
|
61
|
+
end
|
62
|
+
|
63
|
+
protected
|
64
|
+
|
65
|
+
attr_accessor :attribute_name, :check_column_default, :default_value
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def column
|
70
|
+
@column ||= table_columns.find { |column| column.name == attribute_name.to_s }
|
71
|
+
end
|
72
|
+
|
73
|
+
def column_configured_for_default_value?
|
74
|
+
return true unless @check_column_default
|
75
|
+
default_column_value == default_value
|
76
|
+
end
|
77
|
+
|
78
|
+
def column_type
|
79
|
+
@column_type ||= ::ActiveModel::Type.lookup(column.type)
|
80
|
+
end
|
81
|
+
|
82
|
+
def default_column_value
|
83
|
+
column_type.deserialize(column.default)
|
84
|
+
end
|
85
|
+
|
86
|
+
def default_value_found?
|
87
|
+
value_for_attribute == default_value
|
88
|
+
end
|
89
|
+
|
90
|
+
def value_for_attribute
|
91
|
+
@subject.send(attribute_name)
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
data/lib/phillumeny/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: phillumeny
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Deering
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-04-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -47,6 +47,10 @@ extra_rdoc_files: []
|
|
47
47
|
files:
|
48
48
|
- lib/phillumeny.rb
|
49
49
|
- lib/phillumeny/active_model.rb
|
50
|
+
- lib/phillumeny/active_model/validate_presence_of_any.rb
|
51
|
+
- lib/phillumeny/active_record.rb
|
52
|
+
- lib/phillumeny/active_record/cover_query_with_indexes.rb
|
53
|
+
- lib/phillumeny/active_record/have_default_value_of.rb
|
50
54
|
- lib/phillumeny/factory_bot.rb
|
51
55
|
- lib/phillumeny/version.rb
|
52
56
|
homepage: https://github.com/mdeering/phillumeny
|
@@ -68,7 +72,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
68
72
|
version: '0'
|
69
73
|
requirements: []
|
70
74
|
rubyforge_project:
|
71
|
-
rubygems_version: 2.7.
|
75
|
+
rubygems_version: 2.7.6
|
72
76
|
signing_key:
|
73
77
|
specification_version: 4
|
74
78
|
summary: Collection of RSpec matchers for verbose testers.
|