validation_scopes 0.4.1 → 0.5.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.
data/CHANGELOG CHANGED
@@ -1,3 +1,10 @@
1
+ == Unreleased
2
+
3
+ * Fix for memory leak described at http://siliconsenthil.in/blog/2013/01/19/validation-scopes-leaks-memory/
4
+ * Cleaned up .gemspec and removed unnecessary files from distribution
5
+ * Set up Gemfile for development
6
+ * Simplified test suite to use pure minitest
7
+
1
8
  == 0.4.1 2012-04-15
2
9
 
3
10
  * Rails 3.1 and 3.2 compatibility
data/README.md CHANGED
@@ -60,10 +60,20 @@ does not accept an `ActiveRecord::Errors` object directly. Instead you need to
60
60
 
61
61
  ## Compatibility
62
62
 
63
- The current version should work for Rails 3 on Ruby 1.8 or 1.9; Battle-tested in the former.
63
+ The current version should work for Rails >= 3.0 and Ruby >= 1.9.2.
64
+
65
+ For Rails 3 and Ruby 1.8.x use version 0.4.x, however **beware there is a memory leak in this version** as described
66
+ [here](http://siliconsenthil.in/blog/2013/01/19/validation-scopes-leaks-memory/)
64
67
 
65
68
  For Rails 2 see the 0.3.x version of the gem which is maintained on the [rails2
66
- branch](https://github.com/dasil003/validation_scopes/tree/rails2)
69
+ branch](https://github.com/gtd/validation_scopes/tree/rails2)
70
+
71
+ Why no Ruby 1.8.7 support? Because fixing the memory leak was easy in Ruby 1.9 by removing the deferred proxy class
72
+ definition, however this does not work in Ruby 1.8.7 because the ActiveRecord objects methods are not found. The
73
+ development of this was described in a [blog article about the design process](http://www.darwinweb.net/articles/80).
74
+ I didn't take the time to figure out why this started working in Ruby 1.9.x (smells like something to do with
75
+ `instance_eval`) but it does, and I have no inclination to fix issues in 1.8.x anymore. If you happen to know why
76
+ offhand though, I'd be glad to hear the reason.
67
77
 
68
78
 
69
79
  ## Installation
@@ -85,25 +95,12 @@ Outside of Rails:
85
95
  require 'validation_scopes'
86
96
 
87
97
 
88
- ## Caveats
89
-
90
- Due to the use of a proxy DelegateClass to store each additional set of validations, and some heavy meta-programming to
91
- tie it all together with a clean API, there are likely to be some weird edge cases. Please let me know if you discover
92
- anything wonky. I believe the opacity of the solution is worth the convenience it provides in exposing the entirety of
93
- the Validations API.
94
-
95
98
  ### Don't use private methods
96
99
 
97
100
  Because the any validation method supplied as a symbol (eg. `validate :verify_something`) is actually running in the
98
101
  context of a delegate class, private methods won't work as they would in standard validations.
99
102
 
100
103
 
101
- ## Implementation
102
-
103
- I had a lot of fun writing this gem even though the result isn't exactly a shining example elegant code. An [article
104
- about the design process](http://www.darwinweb.net/articles/80) is on my blog.
105
-
106
-
107
104
  ## TODO
108
105
 
109
106
  * In Rails 3 validations are no longer coupled to ActiveRecord. Although the current version of the gem uses
@@ -112,4 +109,4 @@ about the design process](http://www.darwinweb.net/articles/80) is on my blog.
112
109
 
113
110
  ## Copyright
114
111
 
115
- Copyright (c) 2010,2011 Gabe da Silveira. See LICENSE for details.
112
+ Copyright (c) 2010-2013 Gabe da Silveira. See LICENSE for details.
@@ -8,50 +8,38 @@ module ValidationScopes
8
8
 
9
9
  module ClassMethods
10
10
  def validation_scope(scope)
11
- base_class = self
12
11
  @all_scopes ||= []
13
- class << self
14
- def all_scopes=(scope)
15
- @all_scopes << scope
16
- end
12
+ @all_scopes << scope
17
13
 
18
- def all_scopes
19
- @all_scopes
20
- end
14
+ def self.all_scopes
15
+ @all_scopes
21
16
  end
22
- self.all_scopes = scope
23
- deferred_proxy_class_declaration = Proc.new do
24
- proxy_class = Class.new(DelegateClass(base_class)) do
25
- include ActiveModel::Validations
26
17
 
27
- def initialize(record)
28
- @base_record = record
29
- super(record)
30
- end
18
+ base_class = self
31
19
 
32
- # Hack since DelegateClass doesn't seem to be making AR::Base class methods available.
33
- define_method("errors") do
34
- @errors ||= ActiveModel::Errors.new(@base_record)
35
- end
20
+ proxy_class = Class.new(DelegateClass(base_class)) do
21
+ include ActiveModel::Validations
36
22
 
37
- # Hacks to support dynamic_model helpers
38
- def to_model
39
- self
40
- end
41
-
42
- # Rails 3 default implementation of model_name blows up for anonymous classes
43
- class << self; self; end.class_eval do
44
- define_method(:model_name) do
45
- base_class.model_name
46
- end
47
- end
23
+ def initialize(record)
24
+ @base_record = record
25
+ super(record)
48
26
  end
49
27
 
50
- yield proxy_class
28
+ # Hacks to support dynamic_model helpers
29
+ def to_model
30
+ self
31
+ end
51
32
 
52
- proxy_class
33
+ # Rails 3 default implementation of model_name blows up for anonymous classes
34
+ class << self; self; end.class_eval do
35
+ define_method(:model_name) do
36
+ base_class.model_name
37
+ end
38
+ end
53
39
  end
54
40
 
41
+ yield proxy_class
42
+
55
43
  define_method(scope) do
56
44
  send("validation_scope_proxy_for_#{scope}").errors
57
45
  end
@@ -66,8 +54,7 @@ module ValidationScopes
66
54
 
67
55
  define_method("init_validation_scope_for_#{scope}") do
68
56
  unless instance_variable_defined?("@#{scope}")
69
- klass = deferred_proxy_class_declaration.call
70
- instance_variable_set("@#{scope}", klass.new(self))
57
+ instance_variable_set("@#{scope}", proxy_class.new(self))
71
58
  end
72
59
  end
73
60
 
metadata CHANGED
@@ -1,93 +1,102 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: validation_scopes
3
- version: !ruby/object:Gem::Version
4
- hash: 13
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.5.1
5
5
  prerelease:
6
- segments:
7
- - 0
8
- - 4
9
- - 1
10
- version: 0.4.1
11
6
  platform: ruby
12
- authors:
7
+ authors:
13
8
  - Gabe da Silveira
14
9
  autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
-
18
- date: 2012-04-15 00:00:00 Z
19
- dependencies:
20
- - !ruby/object:Gem::Dependency
21
- name: shoulda
12
+ date: 2013-02-05 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activerecord
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '3.0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '3.0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
22
39
  prerelease: false
23
- requirement: &id001 !ruby/object:Gem::Requirement
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: sqlite3
48
+ requirement: !ruby/object:Gem::Requirement
24
49
  none: false
25
- requirements:
26
- - - ">="
27
- - !ruby/object:Gem::Version
28
- hash: 3
29
- segments:
30
- - 0
31
- version: "0"
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
32
54
  type: :development
33
- version_requirements: *id001
34
- description: Define additional sets of validations beyond the standard "errors" that is tied to the ActiveRecord life-cycle. These additional sets can be defined with all the standard ActiveRecord::Validation macros, and the resulting collection is a standard ActiveRecord::Errors object.
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ description: Define additional sets of validations beyond the standard "errors" that
63
+ is tied to the ActiveRecord life-cycle. These additional sets can be defined with
64
+ all the standard ActiveRecord::Validation macros, and the resulting collection is
65
+ a standard ActiveRecord::Errors object.
35
66
  email: gabe@websaviour.com
36
67
  executables: []
37
-
38
68
  extensions: []
39
-
40
- extra_rdoc_files:
69
+ extra_rdoc_files:
41
70
  - LICENSE
42
71
  - README.md
43
- files:
44
- - .document
72
+ files:
45
73
  - CHANGELOG
46
74
  - LICENSE
47
75
  - README.md
48
- - Rakefile
49
- - VERSION
50
76
  - lib/validation_scopes.rb
51
- - test/db/schema.rb
52
- - test/fixtures/books.yml
53
- - test/fixtures/users.yml
54
- - test/helper.rb
55
- - test/models/book.rb
56
- - test/models/user.rb
57
- - test/test_validation_scopes.rb
58
- - validation_scopes.gemspec
59
- homepage: http://github.com/dasil003/validation_scopes
77
+ homepage: http://github.com/gtd/validation_scopes
60
78
  licenses: []
61
-
62
79
  post_install_message:
63
80
  rdoc_options: []
64
-
65
- require_paths:
81
+ require_paths:
66
82
  - lib
67
- required_ruby_version: !ruby/object:Gem::Requirement
83
+ required_ruby_version: !ruby/object:Gem::Requirement
68
84
  none: false
69
- requirements:
70
- - - ">="
71
- - !ruby/object:Gem::Version
72
- hash: 3
73
- segments:
74
- - 0
75
- version: "0"
76
- required_rubygems_version: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ! '>='
87
+ - !ruby/object:Gem::Version
88
+ version: 1.9.2
89
+ required_rubygems_version: !ruby/object:Gem::Requirement
77
90
  none: false
78
- requirements:
79
- - - ">="
80
- - !ruby/object:Gem::Version
81
- hash: 3
82
- segments:
83
- - 0
84
- version: "0"
91
+ requirements:
92
+ - - ! '>='
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
85
95
  requirements: []
86
-
87
96
  rubyforge_project:
88
- rubygems_version: 1.8.22
97
+ rubygems_version: 1.8.24
89
98
  signing_key:
90
99
  specification_version: 3
91
- summary: Create sets of validations independent of the life-cycle of an ActiveRecord object
100
+ summary: Create sets of validations independent of the life-cycle of an ActiveRecord
101
+ object
92
102
  test_files: []
93
-
data/.document DELETED
@@ -1,5 +0,0 @@
1
- README.rdoc
2
- lib/**/*.rb
3
- bin/*
4
- features/**/*.feature
5
- LICENSE
data/Rakefile DELETED
@@ -1,26 +0,0 @@
1
- begin
2
- require 'jeweler'
3
- Jeweler::Tasks.new do |gem|
4
- gem.name = "validation_scopes"
5
- gem.summary = %Q{Create sets of validations independent of the life-cycle of an ActiveRecord object}
6
- gem.description = %Q{Define additional sets of validations beyond the standard "errors" that is tied to the ActiveRecord life-cycle. These additional sets can be defined with all the standard ActiveRecord::Validation macros, and the resulting collection is a standard ActiveRecord::Errors object.}
7
- gem.email = "gabe@websaviour.com"
8
- gem.homepage = "http://github.com/dasil003/validation_scopes"
9
- gem.authors = ["Gabe da Silveira"]
10
- gem.add_development_dependency "shoulda", ">= 0"
11
- end
12
- Jeweler::GemcutterTasks.new
13
- rescue LoadError
14
- puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
15
- end
16
-
17
- require 'rake/testtask'
18
- Rake::TestTask.new(:test) do |test|
19
- test.libs << %w(lib test)
20
- test.pattern = 'test/**/test_*.rb'
21
- test.verbose = true
22
- end
23
-
24
- task :test => :check_dependencies
25
-
26
- task :default => :test
data/VERSION DELETED
@@ -1 +0,0 @@
1
- 0.4.1
@@ -1,16 +0,0 @@
1
- ActiveRecord::Schema.define do
2
- create_table "users" do |t|
3
- t.column "name", :string
4
- t.column "email", :string
5
- t.column "age", :integer
6
- t.column "bio", :text
7
- t.column "sponsor_id", :integer
8
- end
9
-
10
- create_table "books" do |t|
11
- t.column "title", :string
12
- t.column "author", :string
13
- t.column "isbn", :integer
14
- t.column "user_id", :integer
15
- end
16
- end
@@ -1,12 +0,0 @@
1
- one:
2
- id: 1
3
- title: Feature Selection for Knowledge Discovery and Data Mining
4
- author: Huan Liu
5
- isbn: 9780792381983
6
- user_id: 1
7
- two:
8
- id: 2
9
- title: SSL and TLS - theory and practice
10
- author: Rolf Oppliger
11
- isbn: 9781596934474
12
- user_id: 1
@@ -1,13 +0,0 @@
1
- one:
2
- id: 1
3
- name: Gabe da Silveira
4
- email: gabe@websaviour.com
5
- age: 31
6
- bio: Technical web creative
7
- sponsored:
8
- id: 2
9
- name: Sven Pulpello
10
- email: sven@funk.com
11
- age: 3
12
- bio: Alter-ego extraordinaire
13
- sponsor_id: 1
@@ -1,21 +0,0 @@
1
- require 'test/unit'
2
- require 'shoulda'
3
-
4
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
5
- $LOAD_PATH.unshift(File.dirname(__FILE__))
6
- require 'validation_scopes'
7
-
8
- ActiveRecord::Base.establish_connection(
9
- :adapter => "sqlite3",
10
- :database => ":memory:"
11
- )
12
-
13
- require 'db/schema.rb'
14
-
15
- Dir['./test/models/*.rb'].each { |f| require f }
16
-
17
- require 'active_record/fixtures'
18
-
19
- fixtures_constant = defined?(ActiveRecord::Fixtures) ? ActiveRecord::Fixtures : Fixtures
20
-
21
- fixtures_constant.create_fixtures('test/fixtures/', ActiveRecord::Base.connection.tables)
@@ -1,13 +0,0 @@
1
- class Book < ActiveRecord::Base
2
- belongs_to :user
3
-
4
- validates_presence_of :title
5
-
6
- validation_scope :warnings_book do |s|
7
- s.validates_presence_of :author
8
- end
9
-
10
- validation_scope :alerts_book do |s|
11
- s.validates_presence_of :isbn
12
- end
13
- end
@@ -1,26 +0,0 @@
1
- class User < ActiveRecord::Base
2
- belongs_to :sponsor, :class_name => 'User'
3
- has_many :books
4
-
5
- validates_presence_of :name
6
-
7
- validation_scope :warnings do |s|
8
- s.validates_presence_of :email
9
- s.validates_format_of :email, :with => %r{\A.+@.+\Z}
10
- s.validates_inclusion_of :age, :in => 0..99
11
- s.validate do |r|
12
- if r.sponsor_id.present? && r.sponsor.nil?
13
- r.warnings.add(:sponsor_id, "Sponsor ID was defined but record not present")
14
- end
15
- end
16
- end
17
-
18
- validation_scope :alerts do |s|
19
- s.validate :age_under_100
20
- s.validate { |r| r.alerts.add(:email, "We have a hotmail user.") if r.email =~ %r{@hotmail\.com\Z} }
21
- end
22
-
23
- def age_under_100
24
- alerts.add(:base, "We have a centenarian on our hands") if age && age >= 100
25
- end
26
- end
@@ -1,93 +0,0 @@
1
- require 'helper'
2
-
3
- class TestValidationScopes < Test::Unit::TestCase
4
- context "A model with warnings and alerts" do
5
- setup do
6
- @user = User.find(1)
7
- end
8
-
9
- should "have warnings defined" do
10
- assert_nothing_raised { @user.warnings }
11
- end
12
-
13
- should "check for warnings and find none" do
14
- assert ! @user.has_warnings?
15
- assert @user.no_warnings?
16
- end
17
-
18
- should "raise warning if age set negative" do
19
- @user.age = -1
20
- assert @user.has_warnings?
21
- assert @user.warnings[:age].any?
22
- end
23
-
24
- should "raise warning for inline validation" do
25
- @user.sponsor_id = 12345
26
- assert @user.has_warnings?
27
- assert @user.warnings[:sponsor_id].any?
28
- end
29
-
30
- should "not add warning to main errors instance" do
31
- @user.email = ''
32
- assert @user.has_warnings?
33
- assert @user.valid?
34
- assert @user.errors.empty?
35
- end
36
-
37
- should "not add errors to the warnings instance" do
38
- @user.name = ''
39
- assert @user.invalid?
40
- assert @user.warnings.empty?
41
- end
42
-
43
- context "validating alerts with a private method" do
44
- setup do
45
- @user.age = 100
46
- @user.email = "zappa@hotmail.com"
47
- end
48
-
49
- should "set alerts but not errors" do
50
- assert @user.has_alerts?, "no alerts raised"
51
- assert @user.alerts[:base], "centenarian alert not raised"
52
- assert @user.alerts[:email], "hotmail alert not raised"
53
- assert @user.valid?, "user not valid"
54
- assert @user.errors.empty?, "user errors not empty"
55
- end
56
- end
57
- end
58
-
59
- context "proxy class" do
60
- setup do
61
- @user = User.find(1)
62
- end
63
-
64
- # Because error_messages_for in dynamic_model gem calls this and delegation
65
- # causes base object to be returned, thus losing scoped errors.
66
- should "return self for to_model" do
67
- assert_equal @user.validation_scope_proxy_for_warnings.class,
68
- @user.validation_scope_proxy_for_warnings.to_model.class
69
- end
70
-
71
- should "return model_name of base_class" do
72
- assert_equal 'User',
73
- @user.validation_scope_proxy_for_warnings.class.model_name
74
- end
75
- end
76
-
77
- context "scopes per model" do
78
- setup do
79
- @user = User.find(1)
80
- @book = Book.find(1)
81
- @book2 = Book.find(2)
82
- end
83
-
84
- should "return all the scopes declared in User model" do
85
- assert_equal [:warnings, :alerts], @user.class.all_scopes
86
- end
87
-
88
- should "return all the scopes declared in Book model" do
89
- assert_equal [:warnings_book, :alerts_book], @book.class.all_scopes
90
- assert_equal [:warnings_book, :alerts_book], @book2.class.all_scopes
91
- end
92
- end
93
- end
@@ -1,53 +0,0 @@
1
- # Generated by jeweler
2
- # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
- # -*- encoding: utf-8 -*-
5
-
6
- Gem::Specification.new do |s|
7
- s.name = "validation_scopes"
8
- s.version = "0.4.1"
9
-
10
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["Gabe da Silveira"]
12
- s.date = "2012-04-15"
13
- s.description = "Define additional sets of validations beyond the standard \"errors\" that is tied to the ActiveRecord life-cycle. These additional sets can be defined with all the standard ActiveRecord::Validation macros, and the resulting collection is a standard ActiveRecord::Errors object."
14
- s.email = "gabe@websaviour.com"
15
- s.extra_rdoc_files = [
16
- "LICENSE",
17
- "README.md"
18
- ]
19
- s.files = [
20
- ".document",
21
- "CHANGELOG",
22
- "LICENSE",
23
- "README.md",
24
- "Rakefile",
25
- "VERSION",
26
- "lib/validation_scopes.rb",
27
- "test/db/schema.rb",
28
- "test/fixtures/books.yml",
29
- "test/fixtures/users.yml",
30
- "test/helper.rb",
31
- "test/models/book.rb",
32
- "test/models/user.rb",
33
- "test/test_validation_scopes.rb",
34
- "validation_scopes.gemspec"
35
- ]
36
- s.homepage = "http://github.com/dasil003/validation_scopes"
37
- s.require_paths = ["lib"]
38
- s.rubygems_version = "1.8.22"
39
- s.summary = "Create sets of validations independent of the life-cycle of an ActiveRecord object"
40
-
41
- if s.respond_to? :specification_version then
42
- s.specification_version = 3
43
-
44
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
45
- s.add_development_dependency(%q<shoulda>, [">= 0"])
46
- else
47
- s.add_dependency(%q<shoulda>, [">= 0"])
48
- end
49
- else
50
- s.add_dependency(%q<shoulda>, [">= 0"])
51
- end
52
- end
53
-