-
Notifications
You must be signed in to change notification settings - Fork 66
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Extend Vets::Model with secondary Common::Base functionality (#19828)
* add comment * Add filterable to Vets::Model (#19869) * add filterable option to vets::attributes * minor refactoring and add filterable_params * linting * remove class instance variables * Add pagination info to Vets::Model (#19876) * add pagination info to vets::model * actually include the module * refactor tests * load pagination on vets::model * convert pagination mixin to concern * fix broken spec * Add sortability to Vets::Model (#19827) * add sortability to vets::model * linting * fix loading file * treat sortable like a concern * Add dirty module to Vets::Model (#19884) * add dirty to vets::model * model cleanup * linting * convert dirty mixin to concern * update initializer * linting
- Loading branch information
1 parent
01d25d0
commit 21f9a23
Showing
9 changed files
with
465 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
# frozen_string_literal: true | ||
|
||
# Intended to only be used with Vets::Model | ||
# inspired by ActiveModel::Dirty | ||
|
||
module Vets | ||
module Model | ||
module Dirty | ||
extend ActiveSupport::Concern | ||
|
||
included do | ||
attr_reader :original_attributes | ||
end | ||
|
||
def initialize(*, **) | ||
super(*, **) if defined?(super) | ||
@original_attributes = attribute_values.dup | ||
end | ||
|
||
def changed? | ||
changes.any? | ||
end | ||
|
||
def changed | ||
changes.keys | ||
end | ||
|
||
def changes | ||
attribute_values.each_with_object({}) do |(key, current_value), result| | ||
original_value = @original_attributes[key] | ||
result[key] = [original_value, current_value] if original_value != current_value | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
# frozen_string_literal: true | ||
|
||
# | ||
# Pagination allows Vets::Model models to set pagination info | ||
# for that model class. | ||
# | ||
# class User | ||
# include Vets::Model | ||
# | ||
# attr_accessor :name, :age | ||
# | ||
# set_pagination per_page: 21, max_per_page: 41 | ||
# | ||
# ... | ||
# end | ||
# | ||
# User.per_page | ||
# => 21 | ||
# | ||
# User.max_per_page | ||
# => 41 | ||
# | ||
|
||
module Vets | ||
module Model | ||
module Pagination | ||
extend ActiveSupport::Concern | ||
|
||
DEFAULT_PER_PAGE = 10 | ||
DEFAULT_MAX_PER_PAGE = 100 | ||
|
||
class_methods do | ||
# rubocop:disable ThreadSafety/ClassInstanceVariable | ||
def set_pagination(per_page:, max_per_page:) | ||
@per_page = per_page | ||
@max_per_page = max_per_page | ||
end | ||
private :set_pagination | ||
|
||
# Provide default values if set_pagination has not been called | ||
def per_page | ||
@per_page || DEFAULT_PER_PAGE | ||
end | ||
|
||
def max_per_page | ||
@max_per_page || DEFAULT_MAX_PER_PAGE | ||
end | ||
# rubocop:enable ThreadSafety/ClassInstanceVariable | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
# frozen_string_literal: true | ||
|
||
# | ||
# Sortable allows Vets::Model models to specify a default sort attribute and direction | ||
# for use with `#sort`. | ||
# | ||
# class User | ||
# include Vets::Model | ||
# | ||
# attr_accessor :name, :age | ||
# | ||
# default_sort_by name: :asc | ||
# | ||
# ... | ||
# end | ||
# | ||
# [user1, user3, user4, user2].sort | ||
#=> [user1, user2, user3, user4] | ||
# | ||
|
||
module Vets | ||
module Model | ||
module Sortable | ||
include Comparable | ||
extend ActiveSupport::Concern | ||
|
||
class_methods do | ||
# sets the default sorting criteria | ||
# required for use with Array#sort | ||
# rubocop:disable ThreadSafety/ClassInstanceVariable | ||
def default_sort_by(sort_criteria) | ||
if sort_criteria.size != 1 | ||
raise ArgumentError, 'Only one attribute and direction can be provided in default_sort_by' | ||
end | ||
|
||
_, direction = sort_criteria.first | ||
raise ArgumentError, 'Direction must be either :asc or :desc' unless %i[asc desc].include?(direction) | ||
|
||
@default_sort_criteria = sort_criteria | ||
end | ||
|
||
def default_sort_criteria | ||
@default_sort_criteria ||= {} | ||
end | ||
# rubocop:enable ThreadSafety/ClassInstanceVariable | ||
end | ||
|
||
def <=>(other) | ||
return 0 unless self.class.default_sort_criteria.any? | ||
|
||
attribute = self.class.default_sort_criteria.keys.first | ||
direction = self.class.default_sort_criteria[attribute] || :asc | ||
|
||
# Validate if the attribute value is comparable | ||
raise ArgumentError, "Attribute '#{attribute}' is not comparable." unless comparable?(attribute) | ||
|
||
comparison_result = public_send(attribute) <=> other.public_send(attribute) | ||
direction == :desc ? -comparison_result : comparison_result | ||
end | ||
|
||
private | ||
|
||
def comparable?(attribute) | ||
value = public_send(attribute) | ||
value.is_a?(Comparable) | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
# frozen_string_literal: true | ||
|
||
require 'rails_helper' | ||
require 'vets/model' | ||
require 'vets/model/dirty' | ||
|
||
RSpec.describe Vets::Model::Dirty do | ||
let(:user_class) do | ||
Class.new do | ||
include Vets::Model | ||
|
||
attr_accessor :name, :email | ||
|
||
def self.attribute_set | ||
%i[name email] | ||
end | ||
end | ||
end | ||
|
||
let(:user) { user_class.new(name: 'Alice', email: '[email protected]') } | ||
|
||
describe '#changed?' do | ||
it 'returns false when no changes have been made' do | ||
expect(user.changed?).to eq(false) | ||
end | ||
|
||
it 'returns true when an attribute has been changed' do | ||
user.name = 'Bob' | ||
expect(user.changed?).to eq(true) | ||
end | ||
|
||
it 'returns false when changes are reverted back to original values' do | ||
user.name = 'Bob' | ||
user.name = 'Alice' | ||
expect(user.changed?).to eq(false) | ||
end | ||
end | ||
|
||
describe '#changed' do | ||
it 'returns an empty array when no changes have been made' do | ||
expect(user.changed).to eq([]) | ||
end | ||
|
||
it 'returns a list of changed attributes when changes have been made' do | ||
user.name = 'Bob' | ||
expect(user.changed).to eq(['name']) | ||
end | ||
|
||
it 'returns a list of all changed attributes after multiple changes' do | ||
user.name = 'Bob' | ||
user.email = '[email protected]' | ||
expect(user.changed).to match_array(%w[name email]) | ||
end | ||
end | ||
|
||
describe '#changes' do | ||
it 'returns an empty hash when no changes have been made' do | ||
expect(user.changes).to eq({}) | ||
end | ||
|
||
it 'returns the changes with the original and current values when an attribute has been changed' do | ||
user.name = 'Bob' | ||
expect(user.changes).to eq({ 'name' => %w[Alice Bob] }) | ||
end | ||
|
||
it 'returns changes for multiple attributes' do | ||
user.name = 'Bob' | ||
user.email = '[email protected]' | ||
expect(user.changes).to include('name' => %w[Alice Bob]) | ||
expect(user.changes).to include('email' => ['[email protected]', '[email protected]']) | ||
end | ||
|
||
it 'returns an empty hash if changes are reverted' do | ||
user.name = 'Bob' | ||
user.name = 'Alice' | ||
expect(user.changes).to eq({}) | ||
end | ||
end | ||
end |
Oops, something went wrong.