do_not_want 0.0.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.
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
+