diff --git a/README.md b/README.md index 4d640f752af79b78bdb2314149b7e220958f9684..f4e3e0c668a13849299cfe2d6eae36564104a8ea 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,29 @@ class ApiAreaController < ApplicationController ... ``` +### Types + +In initializer: + +```ruby +require 'roqua/type' +``` + +In models: + +```ruby +# strip whitespace and nil if blank. +attribute :firstname, :stripped_string +# strip, but don't nil if blank. +attribute :firstname, :stripped_string, allow_empty: true +``` + +#### option 2 + +`require 'roqua/type/stripped_string'` + +`attribute :firstname, Roqua::Type::StrippedString.new(allow_empty: true)` + # Delayed Job activity monitoring To add monitoring of whether Delayed Job keeps picking up jobs or checking for new jobs, add the following line to an initializer: diff --git a/lib/roqua/type.rb b/lib/roqua/type.rb new file mode 100644 index 0000000000000000000000000000000000000000..f2407de8a51c1420f4ca84e382f6a49659dd8351 --- /dev/null +++ b/lib/roqua/type.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +require "active_model/type" + +require_relative "./type/stripped_string" + +ActiveRecord::Type.register(:stripped_string, Roqua::Type::StrippedString) diff --git a/lib/roqua/type/stripped_string.rb b/lib/roqua/type/stripped_string.rb new file mode 100644 index 0000000000000000000000000000000000000000..ef072a98f7bacdbdd4d372c95e1b79f013736f59 --- /dev/null +++ b/lib/roqua/type/stripped_string.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +# Strips whitespace and make nil when string becomes empty. +module Roqua + module Type + class StrippedString < ActiveModel::Type::ImmutableString + # taken from strip_attributes gem + # Unicode invisible and whitespace characters. The POSIX character class + # [:space:] corresponds to the Unicode class Z ("separator"). We also + # include the following characters from Unicode class C ("control"), which + # are spaces or invisible characters that make no sense at the start or end + # of a string: + # U+180E MONGOLIAN VOWEL SEPARATOR + # U+200B ZERO WIDTH SPACE + # U+200C ZERO WIDTH NON-JOINER + # U+200D ZERO WIDTH JOINER + # U+2060 WORD JOINER + # U+FEFF ZERO WIDTH NO-BREAK SPACE + MULTIBYTE_WHITE = "\u180E\u200B\u200C\u200D\u2060\uFEFF" + MULTIBYTE_SPACE = /[[:space:]#{MULTIBYTE_WHITE}]/.freeze + + def initialize(allow_empty: false) + super() + @allow_empty = allow_empty + end + + def cast(value) + return unless value + value = super(value).gsub(/\A#{MULTIBYTE_SPACE}+|#{MULTIBYTE_SPACE}+\z/, "").freeze + value.blank? && !@allow_empty ? nil : value + end + end + end +end diff --git a/spec/roqua/type/stripped_string_spec.rb b/spec/roqua/type/stripped_string_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..2f5bf534d90cbc9a7b806366687477bdc76bfa76 --- /dev/null +++ b/spec/roqua/type/stripped_string_spec.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +require 'spec_helper' +require 'roqua/type/stripped_string' + +RSpec.describe Roqua::Type::StrippedString do + let(:cls) do + Class.new(ActiveRecord::Base) do + self.table_name = 'delayed_jobs' + + attribute :locked_by, Roqua::Type::StrippedString.new + attribute :queue, Roqua::Type::StrippedString.new(allow_empty: true) + end + end + + it "strips whitespace from outside and not inside" do + expect(described_class.new.cast(" some name \n \t \u180E\u200B\u200C\u200D\u2060\uFEFF ")).to eq "some name" + end + + it "nils empty values by default" do + expect(described_class.new.cast(" \n \t ")).to eq nil + end + + it "keeps value empty if allow_empty is true" do + expect(described_class.new(allow_empty: true).cast(" \n \t ")).to eq "" + end + + it "works on active_record" do + instance = cls.new(locked_by: " \n \t ", queue: " \n \t ", cron: " \n \t ") + expect(instance.locked_by).to be_nil + expect(instance.queue).to eq "" + expect(instance.cron).to eq " \n \t " + end +end