do_not_want 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
File without changes
@@ -0,0 +1,5 @@
1
+ # donotwant Changelog
2
+
3
+ ### 0.0.1 / 2011-08-19
4
+
5
+ * Project Created
@@ -0,0 +1,8 @@
1
+ CHANGELOG.md
2
+ Manifest.txt
3
+ README.md
4
+ Rakefile
5
+ lib/do_not_want.rb
6
+ spec/do_not_want_spec.rb
7
+ spec/gems/fake_gem.rb
8
+ spec/rails_integration_spec.rb
@@ -0,0 +1,47 @@
1
+ # Do Not Want
2
+
3
+ * http://github.com/garybernhardt/do_not_want
4
+
5
+ ## DESCRIPTION:
6
+
7
+ Several methods in ActiveRecord skip validations, callbacks, or both. In my extremely humble but also extremely correct opinion, it's too easy to accidentally use these.
8
+
9
+ Do Not Want kills those methods dead so you won't cut yourself on them:
10
+
11
+ >> User.new.update_attribute(:foo, 5)
12
+ DoNotWant::NotSafe: User#update_attribute isn't safe because it skips validation
13
+
14
+ ## Why Do It Do It?
15
+
16
+ In my experience, even experienced Rails developers don't know which ActiveRecord methods skip validations and callbacks. Quick: which of `decrement`, `decrement!`, and `decrement_counter` skip which? (Hint: they're all different.)
17
+
18
+ ## How Do It Do It?
19
+
20
+ It `define_method`s them away.
21
+
22
+ But! Calls to the unsafe methods are allowed from within gems. This keeps Rails from breaking, and allows third-party code to do as it pleases while keeping your app as jank-free as possible.
23
+
24
+ The disabled instance methods are:
25
+
26
+ decrement
27
+ decrement!
28
+ increment
29
+ ncrement!
30
+ toggle
31
+ toggle!
32
+ update_attribute
33
+
34
+ The disabled class methods are:
35
+
36
+ decrement_counter
37
+ delete
38
+ delete_all
39
+ find_by_sql
40
+ increment_counter
41
+ update_all
42
+ update_counters
43
+
44
+ The particular transgressions that these methods make are documented in the source.
45
+
46
+ The Rails [ActiveRecord guide](http://guides.rubyonrails.org/active_record_validations_callbacks.html#skipping-validations) contains lists about methods that skip validation and callbacks. That's where this list came from.
47
+
@@ -0,0 +1,22 @@
1
+ require 'rubygems'
2
+ require 'hoe'
3
+
4
+ Hoe.plugin :git, :doofus, :seattlerb
5
+
6
+ Hoe.spec 'do_not_want' do
7
+ developer 'Gary Bernhardt', 'gary.bernhardt@gmail.com'
8
+
9
+ ### Use markdown for changelog and readme
10
+ self.history_file = 'CHANGELOG.md'
11
+ self.readme_file = 'README.md'
12
+
13
+ ### Test with rspec
14
+ self.extra_dev_deps << [ 'rspec', '>= 2.1.0' ]
15
+ self.testlib = :rspec
16
+
17
+ ### build zip files too
18
+ self.need_zip = true
19
+ end
20
+
21
+ # add pointer so gem test works. bleh.
22
+ task :test => [:spec]
@@ -0,0 +1,80 @@
1
+ module DoNotWant
2
+ VERSION = "0.0.1"
3
+
4
+ # Bad methods and their reasons
5
+
6
+ BAD_INSTANCE_METHODS = {
7
+ :decrement => ["callbacks"],
8
+ :decrement! => ["validation"],
9
+ :increment => ["callbacks"],
10
+ :increment! => ["validation"],
11
+ :toggle => ["callbacks"],
12
+ :toggle! => ["validation"],
13
+ :update_attribute => ["validation"],
14
+ }
15
+ BAD_INSTANCE_METHOD_NAMES = BAD_INSTANCE_METHODS.keys
16
+
17
+ BAD_CLASS_METHODS = {
18
+ :decrement_counter => ["validation", "callbacks"],
19
+ :delete => ["callbacks"],
20
+ :delete_all => ["callbacks"],
21
+ :find_by_sql => ["callbacks"],
22
+ :increment_counter => ["validation", "callbacks"],
23
+ :update_all => ["validation", "callbacks"],
24
+ :update_counters => ["validation", "callbacks"],
25
+ }
26
+ BAD_CLASS_METHOD_NAMES = BAD_CLASS_METHODS.keys
27
+
28
+ class NotSafe < Exception
29
+ def initialize(called_object, called_method, reason)
30
+ class_name = called_object.class.name
31
+ method_name = called_method.to_s
32
+
33
+ method_description = if called_object.is_a?(Class)
34
+ "#{called_object.name}.#{method_name}"
35
+ else
36
+ "#{class_name}##{method_name}"
37
+ end
38
+
39
+ super "#{method_description} isn't safe because %s" % [
40
+ reason
41
+ ]
42
+ end
43
+ end
44
+
45
+ def self.should_validate_for_caller(caller)
46
+ /\/gems\//.match(caller[0])
47
+ end
48
+ end
49
+
50
+ class Object
51
+ def self.do_not_want!(method_name, reason)
52
+ original_method_name = ('do_not_want_original_' + method_name.to_s).to_sym
53
+ self.send :alias_method, original_method_name, method_name
54
+
55
+ self.send :define_method, method_name do |*args|
56
+ original_method_name = ('do_not_want_original_' + method_name.to_s).to_sym
57
+ use_real_method = DoNotWant.should_validate_for_caller(caller)
58
+ if use_real_method
59
+ return self.send original_method_name, *args
60
+ end
61
+ raise DoNotWant::NotSafe.new(self, method_name, reason)
62
+ end
63
+ end
64
+ end
65
+
66
+ module ActiveRecord
67
+ class Base
68
+
69
+ DoNotWant::BAD_INSTANCE_METHODS.each do |method_name, reasons|
70
+ do_not_want!(method_name, "it skips #{reasons.join(' and ')}")
71
+ end
72
+
73
+ class << self
74
+ DoNotWant::BAD_CLASS_METHODS.each do |method_name, reasons|
75
+ do_not_want!(method_name, "it skips #{reasons.join(' and ')}")
76
+ end
77
+ end
78
+ end
79
+ end
80
+
@@ -0,0 +1,46 @@
1
+ require 'active_record'
2
+ require 'do_not_want'
3
+ require 'gems/fake_gem'
4
+
5
+ class Walrus
6
+ def be_killed_by!(killer, reason)
7
+ die!
8
+ "killed by #{killer} because #{reason}"
9
+ end
10
+
11
+ def die!
12
+ end
13
+
14
+ do_not_want! :be_killed_by!, 'because dying sucks'
15
+ end
16
+
17
+ describe 'do not want' do
18
+ let(:walrus) { Walrus.new }
19
+
20
+ context "method calls" do
21
+ it "raises an error for unwanted method calls" do
22
+ walrus.should_not_receive(:die!)
23
+ expect do
24
+ walrus.be_killed_by!
25
+ end.to raise_error(DoNotWant::NotSafe)
26
+ end
27
+
28
+ it "lets other methods through" do
29
+ walrus.class.should == Walrus
30
+ end
31
+ end
32
+
33
+ context "caller filtering" do
34
+ it "ignores calls from gems" do
35
+ walrus.should_receive(:die!)
36
+ expect do
37
+ kill_walrus_from_gem(walrus)
38
+ end.not_to raise_error
39
+ end
40
+
41
+ it "passes arguments" do
42
+ kill_walrus_from_gem(walrus).should == 'killed by kitty because kitty is angry'
43
+ end
44
+ end
45
+ end
46
+
@@ -0,0 +1,4 @@
1
+ def kill_walrus_from_gem(walrus)
2
+ walrus.be_killed_by!('kitty', 'kitty is angry')
3
+ end
4
+
@@ -0,0 +1,53 @@
1
+ require 'active_record'
2
+ require 'do_not_want'
3
+
4
+ class Cheese < ActiveRecord::Base
5
+ end
6
+
7
+ describe 'rails integration' do
8
+ before do
9
+ ActiveRecord::Base.establish_connection(
10
+ :adapter => "sqlite3",
11
+ :database => ":memory:")
12
+
13
+ ActiveRecord::Base.connection.create_table(:cheeses) do |t|
14
+ t.string :name
15
+ end
16
+ end
17
+
18
+ let(:cheese) { Cheese.create! }
19
+ it 'rejects unsafe instance methods' do
20
+ DoNotWant::BAD_INSTANCE_METHOD_NAMES.each do |method_name|
21
+ expect do
22
+ cheese.send method_name
23
+ end.to raise_error DoNotWant::NotSafe
24
+ end
25
+ end
26
+
27
+ it 'allows safe instance methods' do
28
+ cheese.reload.should == cheese
29
+ end
30
+
31
+ it 'rejects unsafe class methods' do
32
+ DoNotWant::BAD_CLASS_METHOD_NAMES.each do |method_name|
33
+ expect { Cheese.send method_name }.to raise_error DoNotWant::NotSafe
34
+ end
35
+ end
36
+
37
+ it 'allows safe class methods' do
38
+ Cheese.columns.count.should == 2
39
+ end
40
+
41
+ it 'gives reasons' do
42
+ expect { cheese.decrement }.to raise_error(
43
+ DoNotWant::NotSafe,
44
+ "Cheese#decrement isn't safe because it skips callbacks")
45
+ expect { cheese.decrement! }.to raise_error(
46
+ DoNotWant::NotSafe,
47
+ "Cheese#decrement! isn't safe because it skips validation")
48
+ expect { Cheese.update_all }.to raise_error(
49
+ DoNotWant::NotSafe,
50
+ "Cheese.update_all isn't safe because it skips validation and callbacks")
51
+ end
52
+ end
53
+
metadata ADDED
@@ -0,0 +1,111 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: do_not_want
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Gary Bernhardt
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-08-20 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: rspec
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 11
29
+ segments:
30
+ - 2
31
+ - 1
32
+ - 0
33
+ version: 2.1.0
34
+ type: :development
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: hoe
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ~>
43
+ - !ruby/object:Gem::Version
44
+ hash: 27
45
+ segments:
46
+ - 2
47
+ - 12
48
+ version: "2.12"
49
+ type: :development
50
+ version_requirements: *id002
51
+ description: |-
52
+ Several methods in ActiveRecord skip validations, callbacks, or both. In my extremely humble but also extremely correct opinion, it's too easy to accidentally use these.
53
+
54
+ Do Not Want kills those methods dead so you won't cut yourself on them:
55
+
56
+ >> User.new.update_attribute(:foo, 5)
57
+ DoNotWant::NotSafe: User#update_attribute isn't safe because it skips validation
58
+ email:
59
+ - gary.bernhardt@gmail.com
60
+ executables: []
61
+
62
+ extensions: []
63
+
64
+ extra_rdoc_files:
65
+ - Manifest.txt
66
+ files:
67
+ - CHANGELOG.md
68
+ - Manifest.txt
69
+ - README.md
70
+ - Rakefile
71
+ - lib/do_not_want.rb
72
+ - spec/do_not_want_spec.rb
73
+ - spec/gems/fake_gem.rb
74
+ - spec/rails_integration_spec.rb
75
+ - .gemtest
76
+ homepage: http://github.com/garybernhardt/do_not_want
77
+ licenses: []
78
+
79
+ post_install_message:
80
+ rdoc_options:
81
+ - --main
82
+ - README.md
83
+ require_paths:
84
+ - lib
85
+ required_ruby_version: !ruby/object:Gem::Requirement
86
+ none: false
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ hash: 3
91
+ segments:
92
+ - 0
93
+ version: "0"
94
+ required_rubygems_version: !ruby/object:Gem::Requirement
95
+ none: false
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ hash: 3
100
+ segments:
101
+ - 0
102
+ version: "0"
103
+ requirements: []
104
+
105
+ rubyforge_project: do_not_want
106
+ rubygems_version: 1.8.8
107
+ signing_key:
108
+ specification_version: 3
109
+ summary: Several methods in ActiveRecord skip validations, callbacks, or both
110
+ test_files: []
111
+