forever-alone 0.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b052ede07b7ad7b53c3e94df548f0b037fcdd6de
4
+ data.tar.gz: d376c64a2b1bf946d113320f2b2195bb8316491c
5
+ SHA512:
6
+ metadata.gz: 494cd2f686bfdb9925d38eb84a38b9f9caf8fcd99789cb1546e9c98183c925fc189518b0ba0e2fe108b999a261c3fb16103b6f94728d3814c596d831e66af276
7
+ data.tar.gz: 25d7a78b4fe7eddf6d0cffa359efdd747319fcbb97b0eaa1d14f8fb29c3674750973c61886be1efc4a9a73af57e4537df18e019b5d08b66e1047e2133e5f31f7
@@ -0,0 +1,25 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
23
+ .todo
24
+ .ruby-version
25
+ .ruby-gemset
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
@@ -0,0 +1,14 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - 2.1.0
6
+ - 2.2.0
7
+ - ruby-head
8
+ - jruby
9
+ - rbx-2
10
+ cache:
11
+ - apt
12
+ - bundler
13
+ script:
14
+ - bundle exec rspec spec/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in forever-alone.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Rustam Sharshenov
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,60 @@
1
+ # ForeverAlone
2
+ [![Build Status](https://travis-ci.org/Spalmalo/forever-alone.svg)](https://travis-ci.org/Spalmalo/forever-alone)
3
+
4
+ This gem uses Redis to keep a list of recent text messages. ForeverAlone calculates hex digests of message and stores them into the Redis with a given expiration period.
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ gem 'forever-alone'
11
+
12
+ And then execute:
13
+
14
+ $ bundle
15
+
16
+ Or install it yourself as:
17
+
18
+ $ gem install forever-alone
19
+
20
+ ## Usage
21
+
22
+
23
+ ```ruby
24
+ # init ForeverAlone with default timeout(30 minutes)
25
+ forever_alone = ForeverAlone.new("some message")
26
+
27
+ # or with custom timeout
28
+ forever_alone = ForeverAlone.new("some message", 5.minutes)
29
+
30
+ # check if given message is unique
31
+ forever_alone.unique?
32
+
33
+ # remember this message
34
+ forever_alone.remember
35
+
36
+ # OR just ensure that message is unique and raise ForeverAlone::MessageIsNotUnique error otherwise
37
+ forever_alone.ensure
38
+ ```
39
+
40
+ ## Configuration
41
+
42
+ Here is ForeverAlone default configuration:
43
+
44
+ ```ruby
45
+ ForeverAlone.configure do |config|
46
+ config.digest = Digest::MD5 # you can switch to Digest::SHA1
47
+ config.timeout = 1_800 # default expiration period
48
+ config.namespace = :locks # prefix to Redis keys "locks:df49b60423903e095b80d9b4a92eb065"
49
+ end
50
+ ```
51
+
52
+ You can configure redis connection with `REDIS_URL` environment variable.
53
+
54
+ ## Contributing
55
+
56
+ 1. Fork it ( https://github.com/[my-github-username]/forever-alone/fork )
57
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
58
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
59
+ 4. Push to the branch (`git push origin my-new-feature`)
60
+ 5. Create a new Pull Request
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'forever-alone/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "forever-alone"
8
+ spec.version = ForeverAlone::VERSION
9
+ spec.authors = ["Rustam Sharshenov"]
10
+ spec.email = ["rustam@sharshenov.com"]
11
+ spec.summary = "Ensure text uniqueness in period of time."
12
+ spec.description = "This gem uses Redis to keep a list of recent text messages. ForeverAlone calculates hex digests of message and stores them into the Redis with a given expiration period."
13
+ spec.homepage = "https://github.com/Spalmalo/forever-alone"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "redis", "~> 3.0", "> 3.0"
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.6"
24
+ spec.add_development_dependency "rspec", "~> 3.2", ">= 3.2.0"
25
+ spec.add_development_dependency "fakeredis", "~> 0.5.0"
26
+ end
@@ -0,0 +1,31 @@
1
+ require "digest"
2
+ require "redis"
3
+ require "forever-alone/errors"
4
+ require "forever-alone/configuration"
5
+ require "forever-alone/validator"
6
+ require "forever-alone/version"
7
+
8
+ module ForeverAlone
9
+ class << self
10
+ attr_writer :configuration
11
+ attr_writer :redis
12
+
13
+ def configuration
14
+ @configuration ||= Configuration.new
15
+ end
16
+
17
+ def configure
18
+ yield(configuration)
19
+ end
20
+
21
+ def redis
22
+ @redis ||= Redis.new
23
+ end
24
+
25
+ def new message, timeout=nil
26
+ Validator.new message, timeout
27
+ end
28
+
29
+ end
30
+
31
+ end
@@ -0,0 +1,19 @@
1
+ module ForeverAlone
2
+ class Configuration
3
+
4
+ DEFAULTS = {
5
+ digest: Digest::MD5,
6
+ timeout: 1_800, # 30 minutes
7
+ namespace: :locks
8
+ }.freeze
9
+
10
+ DEFAULTS.keys.each do |key|
11
+ attr_writer key
12
+
13
+ define_method key do
14
+ instance_variable_get("@#{ key }") || DEFAULTS.fetch(key)
15
+ end
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,3 @@
1
+ module ForeverAlone
2
+ class MessageIsNotUnique < StandardError; end
3
+ end
@@ -0,0 +1,40 @@
1
+ module ForeverAlone
2
+ class Validator
3
+
4
+ attr_reader :message, :timeout
5
+
6
+ def initialize message, timeout=nil
7
+ @message = message
8
+ @timeout = timeout || ForeverAlone.configuration.timeout
9
+ end
10
+
11
+ def unique?
12
+ ForeverAlone.redis.get(key).nil?
13
+ end
14
+
15
+ def remember
16
+ ForeverAlone.redis.setex key, timeout, 'foo'
17
+ end
18
+
19
+ def ensure
20
+ raise ForeverAlone::MessageIsNotUnique, self.inspect unless unique?
21
+ remember
22
+ end
23
+
24
+ def key
25
+ @key ||= generate_key
26
+ end
27
+
28
+ private
29
+
30
+ def generate_key
31
+ digest = ForeverAlone.configuration.digest.hexdigest message
32
+
33
+ [
34
+ ForeverAlone.configuration.namespace,
35
+ digest
36
+ ].join(':')
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,3 @@
1
+ module ForeverAlone
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1 @@
1
+ require "forever-alone"
@@ -0,0 +1,48 @@
1
+ describe ForeverAlone do
2
+ describe ".configuration" do
3
+
4
+ subject { described_class.configuration }
5
+
6
+ context "when custom configuration provided" do
7
+ before do
8
+ described_class.configure do |config|
9
+ config.digest = Digest::SHA1
10
+ config.timeout = 300
11
+ config.namespace = 'custom'
12
+ end
13
+ end
14
+
15
+ after do
16
+ described_class.configuration = nil
17
+ end
18
+
19
+ it "should have proper digest" do
20
+ expect(subject.digest).to eq Digest::SHA1
21
+ end
22
+
23
+ it "should have proper timeout" do
24
+ expect(subject.timeout).to eq 300
25
+ end
26
+
27
+ it "should have proper namespace" do
28
+ expect(subject.namespace).to eq 'custom'
29
+ end
30
+ end
31
+
32
+ context "when no custom configuration provided" do
33
+
34
+ it "should have proper digest" do
35
+ expect(subject.digest).to eq Digest::MD5
36
+ end
37
+
38
+ it "should have proper timeout" do
39
+ expect(subject.timeout).to eq 1800
40
+ end
41
+
42
+ it "should have proper namespace" do
43
+ expect(subject.namespace).to eq :locks
44
+ end
45
+
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,15 @@
1
+ describe ForeverAlone do
2
+ describe ".new" do
3
+ subject { described_class.new "message", 300 }
4
+
5
+ it { is_expected.to be_a ForeverAlone::Validator }
6
+
7
+ it "should have proper message" do
8
+ expect(subject.message).to eq "message"
9
+ end
10
+
11
+ it "should have proper timeout" do
12
+ expect(subject.timeout).to eq 300
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,7 @@
1
+ describe ForeverAlone do
2
+ describe ".redis" do
3
+ subject { described_class.redis }
4
+
5
+ it { is_expected.to be_a Redis }
6
+ end
7
+ end
@@ -0,0 +1,30 @@
1
+ describe ForeverAlone::Validator, "#ensure" do
2
+ subject { validator.ensure }
3
+
4
+ let!(:validator) { described_class.new "some message" }
5
+
6
+ before { allow(validator).to receive(:remember) }
7
+
8
+ context "when message is unique" do
9
+ before { allow(validator).to receive(:unique?).and_return true }
10
+
11
+ it "should raise no error" do
12
+ expect { subject }.not_to raise_error
13
+ end
14
+
15
+ it "should remember message" do
16
+ subject
17
+ expect(validator).to have_received(:remember)
18
+ end
19
+
20
+ end
21
+
22
+ context "when message is not unique" do
23
+ before { allow(validator).to receive(:unique?).and_return false }
24
+
25
+ it "should raise ForeverAlone::MessageIsNotUnique error" do
26
+ expect { subject }.to raise_error ForeverAlone::MessageIsNotUnique
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,17 @@
1
+ describe ForeverAlone::Validator, "#key" do
2
+ subject { validator.key }
3
+
4
+ let!(:validator) { described_class.new "some message" }
5
+
6
+ context "when digest is Digest::MD5" do
7
+ before { allow(ForeverAlone.configuration).to receive(:digest).and_return Digest::MD5 }
8
+
9
+ it { is_expected.to eq "locks:df49b60423903e095b80d9b4a92eb065" }
10
+ end
11
+
12
+ context "when digest is Digest::SHA1" do
13
+ before { allow(ForeverAlone.configuration).to receive(:digest).and_return Digest::SHA1 }
14
+
15
+ it { is_expected.to eq "locks:af52b1a96761839824b7b4c0e6cea4b09f2b0710" }
16
+ end
17
+ end
@@ -0,0 +1,14 @@
1
+ describe ForeverAlone::Validator, "#remember" do
2
+ subject { validator.remember }
3
+
4
+ let!(:validator) { described_class.new "some message", 10 }
5
+
6
+ before do
7
+ allow(validator).to receive(:key).and_return "locks:awesome"
8
+ end
9
+
10
+ it "should create new Redis record" do
11
+ subject
12
+ expect(ForeverAlone.redis.get("locks:awesome")).to eq "foo"
13
+ end
14
+ end
@@ -0,0 +1,15 @@
1
+ describe ForeverAlone::Validator, "#unique?" do
2
+ subject { validator.unique? }
3
+
4
+ let!(:validator) { described_class.new "some message", 10 }
5
+
6
+ context "when message is unique for a given period" do
7
+ it { is_expected.to eq true }
8
+ end
9
+
10
+ context "when message is not unique for a given period" do
11
+ before { validator.remember }
12
+
13
+ it { is_expected.to eq false }
14
+ end
15
+ end
@@ -0,0 +1,94 @@
1
+ require 'fakeredis/rspec'
2
+ require 'forever-alone'
3
+
4
+ # This file was generated by the `rspec --init` command. Conventionally, all
5
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
6
+ # The generated `.rspec` file contains `--require spec_helper` which will cause
7
+ # this file to always be loaded, without a need to explicitly require it in any
8
+ # files.
9
+ #
10
+ # Given that it is always loaded, you are encouraged to keep this file as
11
+ # light-weight as possible. Requiring heavyweight dependencies from this file
12
+ # will add to the boot time of your test suite on EVERY test run, even for an
13
+ # individual file that may not need all of that loaded. Instead, consider making
14
+ # a separate helper file that requires the additional dependencies and performs
15
+ # the additional setup, and require it from the spec files that actually need
16
+ # it.
17
+ #
18
+ # The `.rspec` file also contains a few flags that are not defaults but that
19
+ # users commonly want.
20
+ #
21
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
22
+ RSpec.configure do |config|
23
+ # rspec-expectations config goes here. You can use an alternate
24
+ # assertion/expectation library such as wrong or the stdlib/minitest
25
+ # assertions if you prefer.
26
+ config.expect_with :rspec do |expectations|
27
+ # This option will default to `true` in RSpec 4. It makes the `description`
28
+ # and `failure_message` of custom matchers include text for helper methods
29
+ # defined using `chain`, e.g.:
30
+ # be_bigger_than(2).and_smaller_than(4).description
31
+ # # => "be bigger than 2 and smaller than 4"
32
+ # ...rather than:
33
+ # # => "be bigger than 2"
34
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
35
+ end
36
+
37
+ # rspec-mocks config goes here. You can use an alternate test double
38
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
39
+ config.mock_with :rspec do |mocks|
40
+ # Prevents you from mocking or stubbing a method that does not exist on
41
+ # a real object. This is generally recommended, and will default to
42
+ # `true` in RSpec 4.
43
+ mocks.verify_partial_doubles = true
44
+ end
45
+
46
+ # The settings below are suggested to provide a good initial experience
47
+ # with RSpec, but feel free to customize to your heart's content.
48
+ =begin
49
+ # These two settings work together to allow you to limit a spec run
50
+ # to individual examples or groups you care about by tagging them with
51
+ # `:focus` metadata. When nothing is tagged with `:focus`, all examples
52
+ # get run.
53
+ config.filter_run :focus
54
+ config.run_all_when_everything_filtered = true
55
+
56
+ # Limits the available syntax to the non-monkey patched syntax that is
57
+ # recommended. For more details, see:
58
+ # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
59
+ # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
60
+ # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
61
+ config.disable_monkey_patching!
62
+
63
+ # This setting enables warnings. It's recommended, but in some cases may
64
+ # be too noisy due to issues in dependencies.
65
+ config.warnings = true
66
+
67
+ # Many RSpec users commonly either run the entire suite or an individual
68
+ # file, and it's useful to allow more verbose output when running an
69
+ # individual spec file.
70
+ if config.files_to_run.one?
71
+ # Use the documentation formatter for detailed output,
72
+ # unless a formatter has already been configured
73
+ # (e.g. via a command-line flag).
74
+ config.default_formatter = 'doc'
75
+ end
76
+
77
+ # Print the 10 slowest examples and example groups at the
78
+ # end of the spec run, to help surface which specs are running
79
+ # particularly slow.
80
+ config.profile_examples = 10
81
+
82
+ # Run specs in random order to surface order dependencies. If you find an
83
+ # order dependency and want to debug it, you can fix the order by providing
84
+ # the seed, which is printed after each run.
85
+ # --seed 1234
86
+ config.order = :random
87
+
88
+ # Seed global randomization in this process using the `--seed` CLI option.
89
+ # Setting this allows you to use `--seed` to deterministically reproduce
90
+ # test failures related to randomization by passing the same `--seed` value
91
+ # as the one that triggered the failure.
92
+ Kernel.srand config.seed
93
+ =end
94
+ end
metadata ADDED
@@ -0,0 +1,144 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: forever-alone
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Rustam Sharshenov
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-04-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: redis
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.0'
20
+ - - ">"
21
+ - !ruby/object:Gem::Version
22
+ version: '3.0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '3.0'
30
+ - - ">"
31
+ - !ruby/object:Gem::Version
32
+ version: '3.0'
33
+ - !ruby/object:Gem::Dependency
34
+ name: bundler
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '1.6'
40
+ type: :development
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '1.6'
47
+ - !ruby/object:Gem::Dependency
48
+ name: rspec
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '3.2'
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: 3.2.0
57
+ type: :development
58
+ prerelease: false
59
+ version_requirements: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - "~>"
62
+ - !ruby/object:Gem::Version
63
+ version: '3.2'
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: 3.2.0
67
+ - !ruby/object:Gem::Dependency
68
+ name: fakeredis
69
+ requirement: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - "~>"
72
+ - !ruby/object:Gem::Version
73
+ version: 0.5.0
74
+ type: :development
75
+ prerelease: false
76
+ version_requirements: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - "~>"
79
+ - !ruby/object:Gem::Version
80
+ version: 0.5.0
81
+ description: This gem uses Redis to keep a list of recent text messages. ForeverAlone
82
+ calculates hex digests of message and stores them into the Redis with a given expiration
83
+ period.
84
+ email:
85
+ - rustam@sharshenov.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".gitignore"
91
+ - ".rspec"
92
+ - ".travis.yml"
93
+ - Gemfile
94
+ - LICENSE.txt
95
+ - README.md
96
+ - Rakefile
97
+ - forever-alone.gemspec
98
+ - lib/forever-alone.rb
99
+ - lib/forever-alone/configuration.rb
100
+ - lib/forever-alone/errors.rb
101
+ - lib/forever-alone/validator.rb
102
+ - lib/forever-alone/version.rb
103
+ - lib/forever/alone.rb
104
+ - spec/forever-alone/configuration_spec.rb
105
+ - spec/forever-alone/new_spec.rb
106
+ - spec/forever-alone/redis_spec.rb
107
+ - spec/forever-alone/validator/ensure_spec.rb
108
+ - spec/forever-alone/validator/key_spec.rb
109
+ - spec/forever-alone/validator/remember_spec.rb
110
+ - spec/forever-alone/validator/unique_spec.rb
111
+ - spec/spec_helper.rb
112
+ homepage: https://github.com/Spalmalo/forever-alone
113
+ licenses:
114
+ - MIT
115
+ metadata: {}
116
+ post_install_message:
117
+ rdoc_options: []
118
+ require_paths:
119
+ - lib
120
+ required_ruby_version: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ required_rubygems_version: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - ">="
128
+ - !ruby/object:Gem::Version
129
+ version: '0'
130
+ requirements: []
131
+ rubyforge_project:
132
+ rubygems_version: 2.4.5
133
+ signing_key:
134
+ specification_version: 4
135
+ summary: Ensure text uniqueness in period of time.
136
+ test_files:
137
+ - spec/forever-alone/configuration_spec.rb
138
+ - spec/forever-alone/new_spec.rb
139
+ - spec/forever-alone/redis_spec.rb
140
+ - spec/forever-alone/validator/ensure_spec.rb
141
+ - spec/forever-alone/validator/key_spec.rb
142
+ - spec/forever-alone/validator/remember_spec.rb
143
+ - spec/forever-alone/validator/unique_spec.rb
144
+ - spec/spec_helper.rb