expose 0.1.0

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/.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: []