rmm5t/strip_attributes

View on GitHub
README.md

Summary

Maintainability
Test Coverage
# StripAttributes

[![Gem Version](http://img.shields.io/gem/v/strip_attributes.svg)](https://rubygems.org/gems/strip_attributes)
[![Build Status](https://github.com/rmm5t/strip_attributes/workflows/CI/badge.svg?branch=master)](https://github.com/rmm5t/strip_attributes/actions?query=workflow%3ACI)
[![Gem Downloads](https://img.shields.io/gem/dt/strip_attributes.svg)](https://rubygems.org/gems/strip_attributes)

StripAttributes is an ActiveModel extension that automatically strips all
attributes of leading and trailing whitespace before validation. If the
attribute is blank, it strips the value to `nil` by default.

It works by adding a before_validation hook to the record.  By default, all
attributes are stripped of whitespace, but `:only` and `:except`
options can be used to limit which attributes are stripped.  Both options accept
a single attribute (`only: :field`) or arrays of attributes (`except: [:field1, :field2, :field3]`).

It's also possible to skip stripping the attributes altogether per model using the `:if` and `:unless` options.

## Installation

Include the gem in your Gemfile:

```ruby
gem "strip_attributes"
```

## Examples

### Default Behavior

```ruby
class DrunkPokerPlayer < ActiveRecord::Base
  strip_attributes
end
```

### Using `except`

```ruby
# all attributes will be stripped except :boxers
class SoberPokerPlayer < ActiveRecord::Base
  strip_attributes except: :boxers
end
```

### Using `only`

```ruby
# only :shoe, :sock, and :glove attributes will be stripped
class ConservativePokerPlayer < ActiveRecord::Base
  strip_attributes only: [:shoe, :sock, :glove]
end
```

### Using `if`

```ruby
# Only records with odd ids will be stripped
class OddPokerPlayer < ActiveRecord::Base
  strip_attributes if: :strip_me?

  def strip_me?
    id.odd?
  end
end
```

### Using `unless`

```ruby
# strip_attributes will be applied randomly
class RandomPokerPlayer < ActiveRecord::Base
  strip_attributes unless: :strip_me?

  def strip_me?
    [true, false].sample
  end
end
```

### Using `allow_empty`

```ruby
# Empty attributes will not be converted to nil
class BrokePokerPlayer < ActiveRecord::Base
  strip_attributes allow_empty: true
end
```

### Using `collapse_spaces`

```ruby
# Sequential spaces in attributes will be collapsed to one space
class EloquentPokerPlayer < ActiveRecord::Base
  strip_attributes collapse_spaces: true
end
```

### Using `replace_newlines`

```ruby
# Newlines in attributes will be replaced with a space
class EloquentPokerPlayer < ActiveRecord::Base
  strip_attributes replace_newlines: true
end
```

### Using `regex`

```ruby
class User < ActiveRecord::Base
  # Strip off characters defined by RegEx
  strip_attributes only: [:first_name, :last_name], regex: /[^[:alpha:]\s]/

  # Strip off non-integers
  strip_attributes only: :phone, regex: /[^0-9]/

  # Strip off all spaces and keep only alphabetic and numeric characters
  strip_attributes only: :nickname, regex: /[^[:alnum:]_-]/

  # Remove trailing whitespace from a multi-line string
  strip_attributes only: :code, regex: /[[:blank:]]+$/)
end
```

## Usage Patterns

### Other ORMs implementing `ActiveModel`

It also works on other ActiveModel classes, such as [Mongoid](http://mongoid.org/) documents:

```ruby
class User
  include Mongoid::Document
  strip_attributes only: :email
end
```

### Using it with [`ActiveAttr`](https://github.com/cgriego/active_attr)

```ruby
class Person
  include ActiveAttr::Model
  include ActiveModel::Validations::Callbacks

  attribute :name
  attribute :email

  strip_attributes
end

```

### Using it directly

```ruby
# where record is an ActiveModel instance
StripAttributes.strip(record, collapse_spaces: true)

# works directly on Strings too
StripAttributes.strip(" foo \t") #=> "foo"
StripAttributes.strip(" foo   bar", collapse_spaces: true) #=> "foo bar"
```

## Testing

StripAttributes provides an RSpec/Shoulda-compatible matcher for easier
testing of attribute assignment. You can use this with
[RSpec](http://rspec.info/), [Shoulda](https://github.com/thoughtbot/shoulda),
[Minitest-MatchersVaccine](https://github.com/rmm5t/minitest-matchers_vaccine)
(preferred), or
[Minitest-Matchers](https://github.com/wojtekmach/minitest-matchers).

### Setup `spec_helper.rb` or `test_helper.rb`

#### To initialize **RSpec**, add this to your `spec_helper.rb`:

```ruby
require "strip_attributes/matchers"
RSpec.configure do |config|
  config.include StripAttributes::Matchers
end
```

#### To initialize **Shoulda (with test-unit)**, add this to your `test_helper.rb`:

```ruby
require "strip_attributes/matchers"
class Test::Unit::TestCase
  extend StripAttributes::Matchers
end
```

OR if in a Rails environment, you might prefer this:

``` ruby
require "strip_attributes/matchers"
class ActiveSupport::TestCase
  extend StripAttributes::Matchers
end
```

#### To initialize **Minitest-MatchersVaccine**, add this to your `test_helper.rb`:

```ruby
require "strip_attributes/matchers"
class MiniTest::Spec
  include StripAttributes::Matchers
end
```

OR if in a Rails environment, you might prefer this:

``` ruby
require "strip_attributes/matchers"
class ActiveSupport::TestCase
  include StripAttributes::Matchers
end
```

#### To initialize **Minitest-Matchers**, add this to your `test_helper.rb`:

```ruby
require "strip_attributes/matchers"
class MiniTest::Spec
  include StripAttributes::Matchers
end
```

### Writing Tests

**RSpec**:

```ruby
describe User do
  it { is_expected.to strip_attribute(:name).collapse_spaces }
  it { is_expected.to strip_attribute(:name).replace_newlines }
  it { is_expected.to strip_attribute :email }
  it { is_expected.to strip_attributes(:name, :email) }
  it { is_expected.not_to strip_attribute :password }
  it { is_expected.not_to strip_attributes(:password, :encrypted_password)  }
end
```

**Shoulda (with test-unit)**:

```ruby
class UserTest < ActiveSupport::TestCase
  should strip_attribute(:name).collapse_spaces
  should strip_attribute(:name).replace_newlines
  should strip_attribute :email
  should strip_attributes(:name, :email)
  should_not strip_attribute :password
  should_not strip_attributes(:password, :encrypted_password)
end
```

**Minitest-MatchersVaccine**:

```ruby
describe User do
  subject { User.new }

  it "should strip attributes" do
    must strip_attribute(:name).collapse_spaces
    must strip_attribute(:name).replace_newlines
    must strip_attribute :email
    must strip_attributes(:name, :email)
    wont strip_attribute :password
    wont strip_attributes(:password, :encrypted_password)
  end
end
```

**Minitest-Matchers**:

```ruby
describe User do
  subject { User.new }

  must { strip_attribute(:name).collapse_spaces }
  must { strip_attribute(:name).replace_newlines }
  must { strip_attribute :email }
  must { strip_attributes(:name, :email) }
  wont { strip_attribute :password }
  wont { strip_attributes(:password, :encrypted_password) }
end
```

## Support

Submit suggestions or feature requests as a GitHub Issue or Pull
Request (preferred). If you send a pull request, remember to update the
corresponding unit tests.  In fact, I prefer new features to be submitted in the
form of new unit tests.

## Credits

The idea was originally triggered by the information at the (now defunct)
Rails Wiki but was modified from the original to include more idiomatic ruby
and rails support.

## Versioning

Semantic Versioning 2.0 as defined at <http://semver.org>.

## License

[MIT License](https://rmm5t.mit-license.org/)