validation_scopes 0.4.1 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
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
-