expose 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,7 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ .rvmrc
6
+ OLD
7
+ spec/db/*.db
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in expose.gemspec
4
+ gemspec
data/README.rdoc ADDED
@@ -0,0 +1,105 @@
1
+ == Expose
2
+
3
+ Expose allows you to dynamically adjust the 'attr_accessible' or
4
+ 'attr_protected' of a model. This is only for managing mass-assignment
5
+ security, and not overall security.
6
+
7
+ === Model
8
+
9
+ The following would let you mass_assign :sometimes_important when the :state
10
+ is 'new' or 'pending'.
11
+
12
+ class Account < ActiveRecord::Base
13
+ include Expose::Model
14
+
15
+ # name:string
16
+ # sometimes_important:string
17
+ # state:string ... example [:new, :pending, :closed]
18
+
19
+ expose :sometimes_important,
20
+ :if => Proc.new { |account| [:new,:pending].include?(account.state) }
21
+
22
+ # same result as line above (just using)
23
+ expose :sometimes_important, :state => [:new, :pending]
24
+
25
+ # similar to line above
26
+ expose :sometimes_important,
27
+ :unless => Proc.new { |account| [:closed].include?(account.state) }
28
+
29
+ # same as line above
30
+ expose :sometimes_important, :not_state => :closed
31
+
32
+ # using whitelist strategy
33
+ attr_accessible :name
34
+
35
+ # OR, using blacklist strategy
36
+ # attr_protected :sometimes_important
37
+
38
+ end
39
+
40
+ == Notes
41
+
42
+ This gem has only been tested with Rails 3.1.rc3, but should work with Rails
43
+ 3.X. It only uses the hook :mass_assignment_authorizer.
44
+
45
+ == Todo
46
+
47
+ This gem is in the early stages of development, so use at your own risk.
48
+
49
+ Plans/Ideas:
50
+ - add 'protect' version, which does the opposite of 'expose'
51
+ - maybe disable attr_protected. Using this gem shows an interest in
52
+ mass-assignment security. Why not ensure use of a whitelist only
53
+ strategy.
54
+ - add controller version (so that session data can be used, ie: role of
55
+ logged in user)
56
+ - add better error handling and option checking, maybe add some logging
57
+ - do not require ActiveRecord, but rather ActiveModel
58
+ - not require adding 'include Expose::Model'. When I do, the class variable
59
+ '_exposures' is shared by all subclasses of ActiveRecord::Base, and each
60
+ declared model then sees the same '_exposures'.
61
+
62
+ == Installation
63
+
64
+ Install the gem:
65
+
66
+ gem install expose
67
+
68
+ Or add Expose to your Gemfile and bundle it up:
69
+
70
+ gem 'expose'
71
+
72
+ == Options
73
+
74
+ 'expose' handles a series of options. Those are:
75
+
76
+ * :if * - When true, the attribute will be added to whitelist.
77
+
78
+ * :unless * - When false, the attribute will be added to whitelist.
79
+
80
+ * :state * - When in this state, the attribute will be added to whitelist.
81
+
82
+ * :not_state * - When not in this state, the attribute will be added to
83
+ whitelist.
84
+
85
+ == Maintainers
86
+
87
+ * Mark G (http://github.com/attack)
88
+
89
+ == Contributors
90
+
91
+ * you
92
+
93
+ == Influence
94
+
95
+ * trusted-params (https://github.com/ryanb/trusted-params) -
96
+ An ActiveController only version, not compatible with Rails 3.X.
97
+
98
+ == Bugs and Feedback
99
+
100
+ If you discover any bugs or want to drop a line, feel free to create an issue
101
+ on GitHub.
102
+
103
+ http://github.com/attack/expose/issues
104
+
105
+ MIT License. Copyright 2011 Mark G. http://github.com/attack
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ desc 'Default: run specs.'
5
+ task :default => :spec
6
+
7
+ desc 'Test the expose gem.'
8
+ RSpec::Core::RakeTask.new(:spec) do |spec|
9
+ spec.pattern = 'spec/**/*_spec.rb'
10
+ spec.rspec_opts = ["-c"]
11
+ end
data/expose.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "expose/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "expose"
7
+ s.version = Expose::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Mark G"]
10
+ s.email = ["expose@attackcorp.com"]
11
+ s.homepage = "https://github.com/attack/expose"
12
+ s.summary = %q{Simple dynamic configuration of attr_protected}
13
+ s.description = %q{Simple dynamic configuration of attr_protected}
14
+
15
+ s.rubyforge_project = "expose"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {spec}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+
22
+ s.add_dependency("rails", "~> 3.0")
23
+
24
+ s.add_development_dependency("rspec", "~> 2.6")
25
+ s.add_development_dependency('sqlite3-ruby')
26
+ end
@@ -0,0 +1,3 @@
1
+ module Expose
2
+ VERSION = "0.1.0"
3
+ end
data/lib/expose.rb ADDED
@@ -0,0 +1,131 @@
1
+ require "expose/version"
2
+
3
+ module Expose
4
+
5
+ module Model
6
+
7
+ def self.included(base)
8
+ base.class_eval do
9
+ extend ClassMethods
10
+
11
+ class_attribute :_exposures, :instance_writer => false
12
+ self._exposures = Hash.new { |h,k| h[k] = [] }
13
+ end
14
+ end
15
+
16
+ module ClassMethods
17
+ # USAGE
18
+ # expose :attribute_name, :if => Proc.new {|model condition}
19
+ # expose :attribute_name, :state => :new, :state => [:new,:locked]
20
+ # - this will add :param_name to the 'attr_accessible' params if the condition is true
21
+ #
22
+ def expose(*config)
23
+ options = config.extract_options!
24
+ options.symbolize_keys!
25
+ options.assert_valid_keys(:if, :unless, :state, :not_state)
26
+
27
+ # TODO - warnings of improper use
28
+ # :name (attribute)
29
+ # validate in attr_protected or not in attr_accessible, otherwise no need to expose
30
+ #
31
+ # :state + :not_state
32
+ # include warning if any similarities in :state and :not_state, as they would cancel each other out
33
+
34
+ config.each do |attr|
35
+ if self.attribute_method?(attr.to_sym)
36
+ # if _exposures.has_key?(name.to_sym)
37
+ # # log duplication ?
38
+ # end
39
+ _exposures[attr.to_sym] = options
40
+ else
41
+ raise "Expose: invalid attribute - #{name.to_s}"
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ protected
48
+
49
+ def mass_assignment_authorizer(attributes)
50
+ attribute_list = super
51
+ if attribute_list.is_a?(ActiveModel::MassAssignmentSecurity::WhiteList)
52
+ attribute_list += attributes_to_expose
53
+ else
54
+ attribute_list -= attributes_to_expose
55
+ end
56
+ attribute_list
57
+ end
58
+
59
+ def attributes_to_expose
60
+ attributes_to_expose = []
61
+
62
+ # if there are exposures
63
+ if _exposures && _exposures.respond_to?(:each)
64
+ # go through each exposure
65
+ _exposures.each do |k,v|
66
+
67
+ # assume exposure of the attribute
68
+ expose_attr = true
69
+
70
+ # RESPOND to configuration
71
+ #
72
+
73
+ # run through both :if and :unless ... then expose attribute if applicable
74
+ if v.key?(:if) || v.key?(:unless)
75
+ expose_attr = (applicable?(v[:if], true) && applicable?(v[:unless], false))
76
+ end
77
+
78
+ # try to match state
79
+ if v.key?(:state)
80
+ # convert to Array
81
+ allowed_states = v[:state].is_a?(Array) ? v[:state] : [v[:state].to_s]
82
+
83
+ # expose attribute if the current state is in expected states
84
+ expose_attr = ( self.respond_to?(:state) &&
85
+ self.state &&
86
+ allowed_states.include?(self.state.to_s))
87
+ end
88
+
89
+ # try to NOT match state
90
+ if v.key?(:not_state)
91
+ # convert to Array
92
+ disallowed_states = v[:not_state].is_a?(Array) ? v[:not_state] : [v[:not_state].to_s]
93
+
94
+ # expose attribute if the current state is NOT in expected states
95
+ expose_attr = ( self.respond_to?(:state) &&
96
+ ( !self.state ||
97
+ !disallowed_states.include?(self.state.to_s)))
98
+ end
99
+
100
+ # if nothing is preventing this attribute from being exposed (eg. all pass, or no options)
101
+ # then add the attribute to attr_accessible
102
+ attributes_to_expose << k.to_s if expose_attr
103
+ end
104
+ end
105
+
106
+ attributes_to_expose
107
+ end
108
+
109
+ # Evaluates the scope options :if or :unless. Returns true if the proc
110
+ # method, or string evals to the expected value.
111
+ def applicable?(string_proc_or_symbol, expected) #:nodoc:
112
+ case string_proc_or_symbol
113
+ when String
114
+ eval(string_proc_or_symbol) == expected
115
+ when Proc
116
+ string_proc_or_symbol.call(self) == expected
117
+ when Symbol
118
+ send(string_proc_or_symbol) == expected
119
+ else
120
+ true
121
+ end
122
+ end
123
+
124
+ end
125
+
126
+ end
127
+ # This is currently commented out, and therefore you need to use
128
+ # 'include Expose::Model' at the top of any subclass of
129
+ # ActiveRecord::Base where you want to use :expose.
130
+ #
131
+ #ActiveRecord::Base.class_eval { include Expose::Model }
data/spec/db/EMPTY ADDED
@@ -0,0 +1 @@
1
+ This directory is left empty, for testing db file to reside in, but not included
@@ -0,0 +1,174 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Expose' do
4
+
5
+ describe "using attr_accessible strategy" do
6
+ describe "initial setup" do
7
+ before(:each) do
8
+ class User < ActiveRecord::Base
9
+ include Expose::Model
10
+ attr_accessible :name
11
+ attr_accessible :not_important
12
+ end
13
+ end
14
+ subject { User.new }
15
+
16
+ it { should expose(:name) }
17
+ it { should protect(:important) }
18
+ it { should protect(:sometimes_important) }
19
+ it { should expose(:not_important) }
20
+ end
21
+
22
+ describe "expose with no options" do
23
+ before(:each) do
24
+ class User < ActiveRecord::Base
25
+ include Expose::Model
26
+ attr_accessible :name
27
+ attr_accessible :not_important
28
+ expose :sometimes_important
29
+ end
30
+ end
31
+ subject { User.new }
32
+
33
+ it { should expose(:name) }
34
+ it { should protect(:important) }
35
+ it { should expose(:sometimes_important) }
36
+ it { should expose(:not_important) }
37
+ end
38
+
39
+ describe "expose with :if" do
40
+ before(:each) do
41
+ class User < ActiveRecord::Base
42
+ include Expose::Model
43
+ attr_accessible :name
44
+ attr_accessible :not_important
45
+ expose :sometimes_important, :if => Proc.new { |user| user.name == 'example name' }
46
+ end
47
+ end
48
+ subject { User.new }
49
+
50
+ it { should expose(:name) }
51
+ it { should protect(:important) }
52
+ it { should expose(:not_important) }
53
+
54
+ context "when :if is true" do
55
+ subject { User.new(:name => 'example name') }
56
+ it { should expose(:sometimes_important) }
57
+ end
58
+
59
+ context "when :if is false" do
60
+ subject { User.new(:name => 'name') }
61
+ it { should protect(:sometimes_important) }
62
+ end
63
+ end
64
+
65
+ describe "expose with :unless" do
66
+ before(:each) do
67
+ class User < ActiveRecord::Base
68
+ include Expose::Model
69
+ attr_accessible :name
70
+ attr_accessible :not_important
71
+ expose :sometimes_important, :unless => Proc.new { |user| user.name == 'example name' }
72
+ end
73
+ end
74
+ subject { User.new }
75
+
76
+ it { should expose(:name) }
77
+ it { should protect(:important) }
78
+ it { should expose(:not_important) }
79
+
80
+ context "when :unless is true" do
81
+ subject { User.new(:name => 'example name') }
82
+ it { should protect(:sometimes_important) }
83
+ end
84
+
85
+ context "when :unless is false" do
86
+ subject { User.new(:name => 'name') }
87
+ it { should expose(:sometimes_important) }
88
+ end
89
+ end
90
+
91
+ describe "expose with :state" do
92
+ before(:each) do
93
+ class User < ActiveRecord::Base
94
+ include Expose::Model
95
+ attr_accessible :state
96
+ attr_accessible :name
97
+ attr_accessible :not_important
98
+ expose :sometimes_important, :state => 'open'
99
+ end
100
+ end
101
+ subject { User.new }
102
+
103
+ it { should expose(:name) }
104
+ it { should protect(:important) }
105
+ it { should expose(:not_important) }
106
+
107
+ context "when :state matches" do
108
+ subject { User.new(:state => 'open') }
109
+ it { should expose(:sometimes_important) }
110
+ end
111
+
112
+ context "when :state doesn't match" do
113
+ subject { User.new(:state => 'closed') }
114
+ it { should protect(:sometimes_important) }
115
+ end
116
+ end
117
+
118
+ describe "expose with :not_state" do
119
+ before(:each) do
120
+ class User < ActiveRecord::Base
121
+ include Expose::Model
122
+ attr_accessible :state
123
+ attr_accessible :name
124
+ attr_accessible :not_important
125
+ expose :sometimes_important, :not_state => 'closed'
126
+ end
127
+ end
128
+ subject { User.new }
129
+
130
+ it { should expose(:name) }
131
+ it { should protect(:important) }
132
+ it { should expose(:not_important) }
133
+
134
+ context "when :not_state doesn't match" do
135
+ subject { User.new(:state => 'open') }
136
+ it { should expose(:sometimes_important) }
137
+ end
138
+
139
+ context "when :state matches" do
140
+ subject { User.new(:state => 'closed') }
141
+ it { should protect(:sometimes_important) }
142
+ end
143
+ end
144
+
145
+ describe "allow multiple attributes in a single definition" do
146
+ before(:each) do
147
+ class User < ActiveRecord::Base
148
+ include Expose::Model
149
+ attr_accessible :state
150
+ attr_accessible :name
151
+ attr_accessible :not_important
152
+ expose :important, :sometimes_important, :if => Proc.new { |user| user.name == 'example name' }
153
+ end
154
+ end
155
+ subject { User.new }
156
+
157
+ it { should expose(:name) }
158
+ it { should expose(:not_important) }
159
+
160
+ context "when :if is true" do
161
+ subject { User.new(:name => 'example name') }
162
+ it { should expose(:important) }
163
+ it { should expose(:sometimes_important) }
164
+ end
165
+
166
+ context "when :if is false" do
167
+ subject { User.new(:name => 'name') }
168
+ it { should protect(:important) }
169
+ it { should protect(:sometimes_important) }
170
+ end
171
+ end
172
+
173
+ end
174
+ end
@@ -0,0 +1,83 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Expose' do
4
+ describe "handles errors" do
5
+
6
+ describe "initial setup (sanity check)" do
7
+ before(:each) do
8
+ class User < ActiveRecord::Base
9
+ include Expose::Model
10
+ attr_protected :important
11
+ attr_protected :sometimes_important
12
+ end
13
+ end
14
+ subject { User.new }
15
+
16
+ it { should expose(:name) }
17
+ it { should protect(:important) }
18
+ it { should protect(:sometimes_important) }
19
+ it { should expose(:not_important) }
20
+ end
21
+
22
+ describe "expose with invalid name" do
23
+ it "raises error" do
24
+ expect {
25
+ class User < ActiveRecord::Base
26
+ include Expose::Model
27
+ attr_protected :important
28
+ attr_protected :sometimes_important
29
+ expose :invalid_name
30
+ end }.to raise_error
31
+ end
32
+ # it "should skip over invalid name, issure warning" do
33
+ # subject.send(:_exposures).should_not include(:invalid_name)
34
+ # end
35
+ end
36
+
37
+ describe "expose with double declaration" do
38
+ before(:each) do
39
+ class User < ActiveRecord::Base
40
+ include Expose::Model
41
+ attr_protected :important
42
+ attr_protected :sometimes_important
43
+ expose :sometimes_important, :state => 'new'
44
+ expose :sometimes_important, :not_state => 'closed'
45
+ expose :sometimes_important, :state => 'open'
46
+ end
47
+ end
48
+ subject { User.new }
49
+
50
+ it "writes over the previous definitions" do
51
+ subject.send(:_exposures).should include(:sometimes_important)
52
+ subject.send(:_exposures)[:sometimes_important].should include(:state)
53
+ subject.send(:_exposures)[:sometimes_important].should_not include(:not_state)
54
+ subject.send(:_exposures)[:sometimes_important][:state].should == 'open'
55
+ end
56
+ end
57
+
58
+ describe "expose two classes with no interferences" do
59
+ before(:each) do
60
+ class User < ActiveRecord::Base
61
+ include Expose::Model
62
+ attr_protected :important
63
+ attr_protected :sometimes_important
64
+ expose :sometimes_important, :state => 'open'
65
+ end
66
+ class Account < ActiveRecord::Base
67
+ include Expose::Model
68
+ attr_protected :important
69
+ attr_protected :sometimes_important
70
+ expose :important, :state => 'new'
71
+ end
72
+ end
73
+
74
+ it "keeps separate exposure lists" do
75
+ User.send(:_exposures).should include(:sometimes_important)
76
+ User.send(:_exposures).should_not include(:important)
77
+ Account.send(:_exposures).should include(:important)
78
+ Account.send(:_exposures).should_not include(:sometimes_important)
79
+ end
80
+ end
81
+
82
+ end
83
+ end
@@ -0,0 +1,171 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Expose' do
4
+
5
+ describe "using attr_protected strategy" do
6
+ describe "initial setup" do
7
+ before(:each) do
8
+ class User < ActiveRecord::Base
9
+ include Expose::Model
10
+ attr_protected :important
11
+ attr_protected :sometimes_important
12
+ end
13
+ end
14
+ subject { User.new }
15
+
16
+ it { should expose(:name) }
17
+ it { should protect(:important) }
18
+ it { should protect(:sometimes_important) }
19
+ it { should expose(:not_important) }
20
+ end
21
+
22
+ describe "expose with no options" do
23
+ before(:each) do
24
+ class User < ActiveRecord::Base
25
+ include Expose::Model
26
+ attr_protected :important
27
+ attr_protected :sometimes_important
28
+ expose :sometimes_important
29
+ end
30
+ end
31
+ subject { User.new }
32
+
33
+ it { should expose(:name) }
34
+ it { should protect(:important) }
35
+ it { should expose(:sometimes_important) }
36
+ it { should expose(:not_important) }
37
+ end
38
+
39
+ describe "expose with :if" do
40
+ before(:each) do
41
+ class User < ActiveRecord::Base
42
+ include Expose::Model
43
+ attr_protected :important
44
+ attr_protected :sometimes_important
45
+ expose :sometimes_important, :if => Proc.new { |user| user.name == 'example name' }
46
+ end
47
+ end
48
+ subject { User.new }
49
+
50
+ it { should expose(:name) }
51
+ it { should protect(:important) }
52
+ it { should expose(:not_important) }
53
+
54
+ context "when :if is true" do
55
+ subject { User.new(:name => 'example name') }
56
+ it { should expose(:sometimes_important) }
57
+ end
58
+
59
+ context "when :if is false" do
60
+ subject { User.new(:name => 'name') }
61
+ it { should protect(:sometimes_important) }
62
+ end
63
+ end
64
+
65
+ describe "expose with :unless" do
66
+ before(:each) do
67
+ class User < ActiveRecord::Base
68
+ include Expose::Model
69
+ attr_protected :important
70
+ attr_protected :sometimes_important
71
+ expose :sometimes_important, :unless => Proc.new { |user| user.name == 'example name' }
72
+ end
73
+ end
74
+ subject { User.new }
75
+
76
+ it { should expose(:name) }
77
+ it { should protect(:important) }
78
+ it { should expose(:not_important) }
79
+
80
+ context "when :unless is true" do
81
+ subject { User.new(:name => 'example name') }
82
+ it { should protect(:sometimes_important) }
83
+ end
84
+
85
+ context "when :unless is false" do
86
+ subject { User.new(:name => 'name') }
87
+ it { should expose(:sometimes_important) }
88
+ end
89
+ end
90
+
91
+ describe "expose with :state" do
92
+ before(:each) do
93
+ class User < ActiveRecord::Base
94
+ include Expose::Model
95
+ attr_protected :important
96
+ attr_protected :sometimes_important
97
+ expose :sometimes_important, :state => 'open'
98
+ end
99
+ end
100
+ subject { User.new }
101
+
102
+ it { should expose(:name) }
103
+ it { should protect(:important) }
104
+ it { should expose(:not_important) }
105
+
106
+ context "when :state matches" do
107
+ subject { User.new(:state => 'open') }
108
+ it { should expose(:sometimes_important) }
109
+ end
110
+
111
+ context "when :state doesn't match" do
112
+ subject { User.new(:state => 'closed') }
113
+ it { should protect(:sometimes_important) }
114
+ end
115
+ end
116
+
117
+ describe "expose with :not_state" do
118
+ before(:each) do
119
+ class User < ActiveRecord::Base
120
+ include Expose::Model
121
+ attr_protected :important
122
+ attr_protected :sometimes_important
123
+ expose :sometimes_important, :not_state => 'closed'
124
+ end
125
+ end
126
+ subject { User.new }
127
+
128
+ it { should expose(:name) }
129
+ it { should protect(:important) }
130
+ it { should expose(:not_important) }
131
+
132
+ context "when :not_state doesn't match" do
133
+ subject { User.new(:state => 'open') }
134
+ it { should expose(:sometimes_important) }
135
+ end
136
+
137
+ context "when :state matches" do
138
+ subject { User.new(:state => 'closed') }
139
+ it { should protect(:sometimes_important) }
140
+ end
141
+ end
142
+
143
+ describe "allow multiple attributes in a single definition" do
144
+ before(:each) do
145
+ class User < ActiveRecord::Base
146
+ include Expose::Model
147
+ attr_protected :important
148
+ attr_protected :sometimes_important
149
+ expose :important, :sometimes_important, :if => Proc.new { |user| user.name == 'example name' }
150
+ end
151
+ end
152
+ subject { User.new }
153
+
154
+ it { should expose(:name) }
155
+ it { should expose(:not_important) }
156
+
157
+ context "when :if is true" do
158
+ subject { User.new(:name => 'example name') }
159
+ it { should expose(:important) }
160
+ it { should expose(:sometimes_important) }
161
+ end
162
+
163
+ context "when :if is false" do
164
+ subject { User.new(:name => 'name') }
165
+ it { should protect(:important) }
166
+ it { should protect(:sometimes_important) }
167
+ end
168
+ end
169
+
170
+ end
171
+ end
@@ -0,0 +1,41 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ Bundler.setup(:default, :development, :test)
4
+
5
+ require 'rails'
6
+ require 'active_support'
7
+ require 'active_model'
8
+ require 'active_record'
9
+ require 'expose'
10
+ require 'rspec'
11
+
12
+ root = File.expand_path(File.join(File.dirname(__FILE__), '..'))
13
+ ActiveRecord::Base.establish_connection(
14
+ :adapter => "sqlite3",
15
+ :database => "#{root}/spec/db/expose.db"
16
+ )
17
+
18
+ RSpec.configure do |config|
19
+ end
20
+
21
+ # create the tables for the test in the database
22
+ ActiveRecord::Base.connection.execute("DROP TABLE IF EXISTS 'users'")
23
+ ActiveRecord::Base.connection.create_table(:users) do |t|
24
+ t.string :name, :default => 'name'
25
+ t.string :important, :default => 'important'
26
+ t.string :sometimes_important, :default => 'sometimes_important'
27
+ t.string :not_important, :default => 'not_important'
28
+ t.string :state, :default => 'new'
29
+ end
30
+
31
+ ActiveRecord::Base.connection.execute("DROP TABLE IF EXISTS 'accounts'")
32
+ ActiveRecord::Base.connection.create_table(:accounts) do |t|
33
+ t.string :name, :default => 'name'
34
+ t.string :important, :default => 'important'
35
+ t.string :sometimes_important, :default => 'sometimes_important'
36
+ t.string :not_important, :default => 'not_important'
37
+ t.string :state, :default => 'new'
38
+ end
39
+
40
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
41
+
@@ -0,0 +1,46 @@
1
+ RSpec::Matchers.define :expose do |attr|
2
+ match do |subject|
3
+ # expect {
4
+ # current_value = subject.send(attr.to_sym)
5
+ # subject.attributes = {attr.to_sym => (current_value && !current_value.blank? ? (current_value + '_new') : 'new')}
6
+ # }.to change{ subject.send(attr.to_sym) }
7
+ old_value = subject.send(attr.to_sym).to_s
8
+ subject.update_attributes({attr.to_sym => (old_value && !old_value.blank? ? (old_value + '_new') : 'new')})
9
+ subject.reload
10
+ old_value != subject.send(attr.to_sym)
11
+ end
12
+
13
+ failure_message_for_should do |subject|
14
+ "expected that #{subject} does not protect #{attr}, but it does"
15
+ end
16
+
17
+ failure_message_for_should_not do |subject|
18
+ "expected that #{subject} attr_protects #{attr}, but it does not"
19
+ end
20
+
21
+ description do
22
+ "NOT attr_protected :#{attr}"
23
+ end
24
+ end
25
+
26
+ RSpec::Matchers.define :protect do |attr|
27
+ match do |subject|
28
+ old_value = subject.send(attr.to_sym)
29
+ subject.attributes = {attr.to_sym => (old_value && !old_value.blank? ? (old_value + '_new') : 'new')}
30
+ new_value = subject.send(attr.to_sym)
31
+
32
+ old_value == new_value
33
+ end
34
+
35
+ failure_message_for_should do |subject|
36
+ "expected that #{subject} attr_protects #{attr}, but it does not"
37
+ end
38
+
39
+ failure_message_for_should_not do |subject|
40
+ "expected that #{subject} does not protect #{attr}, but it does"
41
+ end
42
+
43
+ description do
44
+ "attr_protected :#{attr}"
45
+ end
46
+ end
metadata ADDED
@@ -0,0 +1,93 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: expose
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Mark G
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-06-20 00:00:00.000000000 -04:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rails
17
+ requirement: &2153270260 !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ~>
21
+ - !ruby/object:Gem::Version
22
+ version: '3.0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: *2153270260
26
+ - !ruby/object:Gem::Dependency
27
+ name: rspec
28
+ requirement: &2153269760 !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '2.6'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: *2153269760
37
+ - !ruby/object:Gem::Dependency
38
+ name: sqlite3-ruby
39
+ requirement: &2153269380 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ! '>='
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ type: :development
46
+ prerelease: false
47
+ version_requirements: *2153269380
48
+ description: Simple dynamic configuration of attr_protected
49
+ email:
50
+ - expose@attackcorp.com
51
+ executables: []
52
+ extensions: []
53
+ extra_rdoc_files: []
54
+ files:
55
+ - .gitignore
56
+ - Gemfile
57
+ - README.rdoc
58
+ - Rakefile
59
+ - expose.gemspec
60
+ - lib/expose.rb
61
+ - lib/expose/version.rb
62
+ - spec/db/EMPTY
63
+ - spec/expose_accessible_spec.rb
64
+ - spec/expose_errors_spec.rb
65
+ - spec/expose_protected_spec.rb
66
+ - spec/spec_helper.rb
67
+ - spec/support/matchers.rb
68
+ has_rdoc: true
69
+ homepage: https://github.com/attack/expose
70
+ licenses: []
71
+ post_install_message:
72
+ rdoc_options: []
73
+ require_paths:
74
+ - lib
75
+ required_ruby_version: !ruby/object:Gem::Requirement
76
+ none: false
77
+ requirements:
78
+ - - ! '>='
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ required_rubygems_version: !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ! '>='
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ requirements: []
88
+ rubyforge_project: expose
89
+ rubygems_version: 1.6.2
90
+ signing_key:
91
+ specification_version: 3
92
+ summary: Simple dynamic configuration of attr_protected
93
+ test_files: []