periscope 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.
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source :rubygems
2
+
3
+ gemspec
@@ -0,0 +1,58 @@
1
+ = Periscope
2
+
3
+ Bring your models' scopes up above the surface.
4
+
5
+ Periscope acts like +attr_accessible+ or +attr_protected+, but for your models' scopes.
6
+
7
+ == The Problem
8
+
9
+ More often than not, the index action in a RESTful Rails controller is expected to do a lot more than simply return all the records for a given model. We ask it to do all sorts of stuff like filtering, sorting and paginating results. Of course, this is typically done using _scopes_.
10
+
11
+ But sometimes it can get ugly building long, complicated chains of scope in the controller, especially when you try to give your users control over the scoping. Picture this:
12
+
13
+ def index
14
+ @articles = Article.scoped
15
+ @articles = @articles.published_after(params[:published_after]) if params.key?(:published_after)
16
+ @articles = @articles.published_before(params[:published_before]) if params.key?(:published_before)
17
+ end
18
+
19
+ You can imagine how bad this would get if more than two scopes were involved.
20
+
21
+ == The Solution
22
+
23
+ With Periscope, you can have this instead:
24
+
25
+ def index
26
+ @articles = Article.periscope(request.query_parameters)
27
+ end
28
+
29
+ The +periscope+ method will find keys in your params matching your scope names and chain your scopes for you.
30
+
31
+ <b>Note:</b> We're using <code>request.query_parameters</code> so we can exclude your controller and action params. <code>request.query_parameters</code> will just return the params that show up after the "?" in the URL.
32
+
33
+ == But Wait!
34
+
35
+ "What if I don't want to make all my scopes publicly accessible?"
36
+
37
+ In your model you can use either the +scope_accessible+ or +scope_protected+ method to specify which scopes you want Periscope to pay attention to.
38
+
39
+ class User < ActiveRecord::Base
40
+ attr_accessible :name, :gender, :salary
41
+
42
+ scope :gender, lambda{|g| where(:gender => g) }
43
+ scope :makes_more_than, lambda{|s| where('users.salary >= ?', s) }
44
+
45
+ scope_accessible :gender
46
+ end
47
+
48
+ And in your controller:
49
+
50
+ class UsersController < ApplicationController
51
+ def index
52
+ @users = User.periscope(request.query_parameters)
53
+ end
54
+ end
55
+
56
+ Now, requests to <code>/users?gender=male</code> will filter results to only male users. But a request to <code>/users?makes_more_than=1000000</code> will return all users, silently ignoring the protected scope.
57
+
58
+ By default, all scopes are protected.
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'periscope'
@@ -0,0 +1,6 @@
1
+ require 'active_support'
2
+ require 'periscope/version'
3
+
4
+ Dir[File.expand_path('../periscope/adapters/**/*.rb', __FILE__)].each{|f| require f }
5
+
6
+ ActiveRecord::Base.send(:include, Periscope::Adapters::ActiveRecord) if defined?(ActiveRecord::Base)
@@ -0,0 +1,67 @@
1
+ require 'active_support/core_ext/class/attribute.rb'
2
+ require 'periscope/permission_set'
3
+
4
+ module Periscope
5
+ module Adapters
6
+ module Abstract
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ class_attribute :_accessible_scopes
11
+ class_attribute :_protected_scopes
12
+ class_attribute :_periscope_authorizer
13
+ end
14
+
15
+ module ClassMethods
16
+ def periscope(*)
17
+ raise NotImplementedError
18
+ end
19
+
20
+ def scope_protected(*scopes)
21
+ self._protected_scopes = protected_scopes + scopes
22
+ self._periscope_authorizer = _protected_scopes
23
+ end
24
+
25
+ alias_method :down_periscope, :scope_protected
26
+
27
+ def scope_accessible(*scopes)
28
+ self._accessible_scopes = accessible_scopes + scopes
29
+ self._periscope_authorizer = _accessible_scopes
30
+ end
31
+
32
+ alias_method :up_periscope, :scope_accessible
33
+
34
+ def protected_scopes
35
+ self._protected_scopes ||= BlackList.new.tap do |list|
36
+ list.logger = logger if respond_to?(:logger)
37
+ end
38
+ end
39
+
40
+ def accessible_scopes
41
+ self._accessible_scopes ||= WhiteList.new(scopes_accessible_by_default).tap do |list|
42
+ list.logger = logger if respond_to?(:logger)
43
+ end
44
+ end
45
+
46
+ def periscope_authorizer
47
+ self._periscope_authorizer ||= accessible_scopes
48
+ end
49
+
50
+ def scopes_accessible_by_default
51
+ []
52
+ end
53
+ end
54
+
55
+ module InstanceMethods
56
+ protected
57
+ def sanitize_for_search(params)
58
+ search_authorizer.sanitize(params)
59
+ end
60
+
61
+ def search_authorizer
62
+ self.class.periscope_authorizer
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,18 @@
1
+ require 'periscope/adapters/abstract'
2
+
3
+ module Periscope
4
+ module Adapters
5
+ module ActiveRecord
6
+ extend ActiveSupport::Concern
7
+ include Abstract
8
+
9
+ module ClassMethods
10
+ def periscope(params = {})
11
+ periscope_authorizer.sanitize(params).inject(scoped) do |chain, (key, value)|
12
+ chain.send(key, value)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,34 @@
1
+ require 'set'
2
+ require 'periscope/sanitizer'
3
+
4
+ module Periscope
5
+ class PermissionSet < Set
6
+ def initialize(values = nil)
7
+ super(values, &:to_s)
8
+ end
9
+
10
+ def +(values)
11
+ super(values.map(&:to_s))
12
+ end
13
+
14
+ def include?(value)
15
+ super(value.to_s)
16
+ end
17
+ end
18
+
19
+ class WhiteList < PermissionSet
20
+ include Sanitizer
21
+
22
+ def deny?(value)
23
+ !include?(value)
24
+ end
25
+ end
26
+
27
+ class BlackList < PermissionSet
28
+ include Sanitizer
29
+
30
+ def deny?(value)
31
+ include?(value)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,27 @@
1
+ module Periscope
2
+ module Sanitizer
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ attr_accessor :logger
7
+ end
8
+
9
+ module InstanceMethods
10
+ def sanitize(params)
11
+ params.reject{|k,v| deny?(k) }.tap do |sanitized|
12
+ debug_protected_scope_removal(params, sanitized)
13
+ end
14
+ end
15
+
16
+ protected
17
+ def debug_protected_scope_removal(params, sanitized)
18
+ removed = params.keys - sanitized.keys
19
+ warn!(removed) if removed.any?
20
+ end
21
+
22
+ def warn!(scopes)
23
+ logger.debug("WARNING: Can't search protected scopes: #{scopes.join(', ')}") if logger
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,3 @@
1
+ module Periscope
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path('../lib', __FILE__)
3
+ require 'periscope/version'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'periscope'
7
+ s.version = Periscope::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ['Steve Richert']
10
+ s.email = ['steve.richert@gmail.com']
11
+ s.homepage = 'https://github.com/laserlemon/periscope'
12
+ s.summary = %(Bring your models' scopes up above the surface.)
13
+ s.description = %(Periscope acts like attr_accessible or attr_protected, but for your models' scopes.)
14
+
15
+ s.rubyforge_project = 'periscope'
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.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 'activesupport', '>= 3.0.0'
23
+
24
+ s.add_development_dependency 'rspec'
25
+ s.add_development_dependency 'sqlite3'
26
+ s.add_development_dependency 'activerecord', '>= 3.0.0'
27
+ end
@@ -0,0 +1,129 @@
1
+ require 'spec_helper'
2
+
3
+ module Periscope
4
+ module Adapters
5
+ describe ActiveRecord do
6
+ it_should_behave_like 'an adapter'
7
+
8
+ describe :periscope do
9
+ subject do
10
+ Class.new(User).tap do |klass|
11
+ klass.class_eval do
12
+ scope :gender, lambda{|g| where(:gender => g) }
13
+ scope :makes, lambda{|s| where('users.salary >= ?', s) }
14
+ scope :rich, where('users.salary >= 1000000')
15
+ end
16
+ end
17
+ end
18
+
19
+ context 'ignores' do
20
+ specify 'all scopes by default' do
21
+ subject.should_receive(:gender).never
22
+ subject.should_receive(:makes).never
23
+
24
+ subject.periscope(:gender => 'male', :makes => 100000)
25
+ end
26
+
27
+ specify 'all scopes when none are accessible' do
28
+ subject.scope_accessible
29
+
30
+ subject.should_receive(:gender).never
31
+ subject.should_receive(:makes).never
32
+
33
+ subject.periscope(:gender => 'male', :makes => 100000)
34
+ end
35
+
36
+ specify 'protected scopes' do
37
+ subject.scope_protected :makes
38
+
39
+ subject.should_receive(:gender).with('male').once
40
+ subject.should_receive(:makes).never
41
+
42
+ subject.periscope(:gender => 'male', :makes => 100000)
43
+ end
44
+ end
45
+
46
+ context 'uses' do
47
+ specify 'all scopes when none are protected' do
48
+ subject.scope_protected
49
+
50
+ subject.should_receive(:gender).with('male').once
51
+ subject.should_receive(:makes).with(100000).once
52
+
53
+ subject.periscope(:gender => 'male', :makes => 100000)
54
+ end
55
+
56
+ specify 'accessible scopes' do
57
+ subject.scope_accessible :gender
58
+
59
+ subject.should_receive(:gender).with('male').once
60
+ subject.should_receive(:makes).never
61
+
62
+ subject.periscope(:gender => 'male', :makes => 100000)
63
+ end
64
+
65
+ specify 'accessible, zero-arity scopes' do
66
+ subject.scope_accessible :rich
67
+
68
+ subject.should_receive(:rich).with(true).once
69
+
70
+ lambda{ subject.periscope(:rich => true) }.should_not raise_error
71
+ end
72
+ end
73
+
74
+ context 'returns' do
75
+ before do
76
+ subject.delete_all
77
+ subject.create(:name => 'Henry', :gender => 'male', :salary => 50_000)
78
+ subject.create(:name => 'Penny', :gender => 'female', :salary => 1_000_000)
79
+ subject.create(:name => 'Sammy', :gender => 'male', :salary => 100_000)
80
+ end
81
+
82
+ let(:params){ {:gender => 'male', :rich => true} }
83
+
84
+ context 'all records' do
85
+ specify 'for no params' do
86
+ subject.periscope.map(&:name).should == %w(Henry Penny Sammy)
87
+ end
88
+
89
+ specify 'for empty params' do
90
+ subject.periscope({}).map(&:name).should == %w(Henry Penny Sammy)
91
+ end
92
+
93
+ specify 'for no accessible scopes' do
94
+ subject.scope_accessible
95
+
96
+ subject.periscope(params).map(&:name).should == %w(Henry Penny Sammy)
97
+ end
98
+
99
+ specify 'for all protected scopes' do
100
+ subject.scope_protected :gender, :rich
101
+
102
+ subject.periscope(params).map(&:name).should == %w(Henry Penny Sammy)
103
+ end
104
+ end
105
+
106
+ context 'scoped results' do
107
+ specify 'for an accessible scope' do
108
+ subject.scope_accessible :rich
109
+
110
+ subject.periscope(params).map(&:name).should == %w(Penny)
111
+ end
112
+
113
+ specify 'for an unprotected scope' do
114
+ subject.scope_protected :gender
115
+
116
+ subject.periscope(params).map(&:name).should == %w(Penny)
117
+ end
118
+
119
+ specify 'for multiple accessible scopes' do
120
+ subject.scope_accessible :gender, :rich
121
+
122
+ subject.periscope(params).should be_empty
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ module Periscope
4
+ describe BlackList do
5
+ it_should_behave_like 'a permission set'
6
+ it_should_behave_like 'a sanitizer'
7
+
8
+ let(:values){ %w(one two three) }
9
+ subject{ described_class.new(values) }
10
+
11
+ it 'denies an included value' do
12
+ subject.deny?(values.first).should == true
13
+ end
14
+
15
+ it 'accepts an excluded value' do
16
+ subject.deny?('four').should == false
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,10 @@
1
+ require 'spec_helper'
2
+
3
+ module Periscope
4
+ describe VERSION do
5
+ it 'is a major/minor/patch version string' do
6
+ should be_a String
7
+ should match /^\d+\.\d+\.\d+$/
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ module Periscope
4
+ describe WhiteList do
5
+ it_should_behave_like 'a permission set'
6
+ it_should_behave_like 'a sanitizer'
7
+
8
+ let(:values){ %w(one two three) }
9
+ subject{ described_class.new(values) }
10
+
11
+ it 'accepts an included value' do
12
+ subject.deny?(values.first).should == false
13
+ end
14
+
15
+ it 'denies an excluded value' do
16
+ subject.deny?('four').should == true
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,14 @@
1
+ Dir[File.expand_path('../support/**/*.rb', __FILE__)].each{|f| require f }
2
+
3
+ require 'periscope'
4
+
5
+ class FakeLogger
6
+ def debug(message)
7
+ output << message
8
+ end
9
+
10
+ def output
11
+ @output ||= []
12
+ end
13
+ end
14
+
@@ -0,0 +1,89 @@
1
+ shared_examples_for 'an adapter' do
2
+ subject do
3
+ Class.new.tap do |klass|
4
+ klass.send(:include, described_class)
5
+ end
6
+ end
7
+
8
+ it 'adds accessible scopes' do
9
+ subject.scope_accessible :current, :expired
10
+
11
+ subject.accessible_scopes.should include(:current)
12
+ subject.accessible_scopes.should include(:expired)
13
+ end
14
+
15
+ it 'adds protected scopes' do
16
+ subject.scope_protected :current, :expired
17
+
18
+ subject.protected_scopes.should include(:current)
19
+ subject.protected_scopes.should include(:expired)
20
+ end
21
+
22
+ it 'stacks accessible scopes' do
23
+ subject.scope_accessible :current
24
+ subject.scope_accessible :expired
25
+
26
+ subject.accessible_scopes.should include(:current)
27
+ subject.accessible_scopes.should include(:expired)
28
+ end
29
+
30
+ it 'stacks protected scopes' do
31
+ subject.scope_protected :current
32
+ subject.scope_protected :expired
33
+
34
+ subject.protected_scopes.should include(:current)
35
+ subject.protected_scopes.should include(:expired)
36
+ end
37
+
38
+ it 'uses accessible scopes if defined last' do
39
+ subject.scope_protected :current
40
+ subject.scope_accessible :expired
41
+
42
+ subject.periscope_authorizer.should be_a(Periscope::WhiteList)
43
+ subject.periscope_authorizer.should == subject.accessible_scopes
44
+ end
45
+
46
+ it 'uses protected scopes if defined last' do
47
+ subject.scope_accessible :current
48
+ subject.scope_protected :expired
49
+
50
+ subject.periscope_authorizer.should be_a(Periscope::BlackList)
51
+ subject.periscope_authorizer.should == subject.protected_scopes
52
+ end
53
+
54
+ it 'defaults to using accessible scopes' do
55
+ subject.periscope_authorizer.should be_a(Periscope::WhiteList)
56
+ subject.periscope_authorizer.should == subject.accessible_scopes
57
+ end
58
+
59
+ it 'has no accesible scopes by default' do
60
+ subject.accessible_scopes.should be_empty
61
+ end
62
+
63
+ it 'has no protected scopes by default' do
64
+ subject.protected_scopes.should be_empty
65
+ end
66
+
67
+ it 'can override the default accessible scopes' do
68
+ subject.class_eval do
69
+ def self.scopes_accessible_by_default
70
+ [:current]
71
+ end
72
+ end
73
+
74
+ subject.accessible_scopes.should include(:current)
75
+ end
76
+
77
+ it 'makes its authorizer available to its instances' do
78
+ subject.scope_accessible :current
79
+
80
+ subject.new.send(:search_authorizer).should == subject.accessible_scopes
81
+ end
82
+
83
+ it 'sanitizes params from its instances' do
84
+ params = {:current => 'yes', :expired => 'no'}
85
+ subject.scope_accessible :current
86
+
87
+ subject.new.send(:sanitize_for_search, params).should == params.slice(:current)
88
+ end
89
+ end
@@ -0,0 +1,17 @@
1
+ require 'active_record'
2
+
3
+ ActiveRecord::Base.establish_connection(
4
+ :adapter => 'sqlite3',
5
+ :database => ':memory:'
6
+ )
7
+
8
+ ActiveRecord::Schema.define do
9
+ create_table :users do |t|
10
+ t.string :name
11
+ t.string :gender
12
+ t.integer :salary
13
+ end
14
+ end
15
+
16
+ class User < ActiveRecord::Base
17
+ end
@@ -0,0 +1,35 @@
1
+ shared_examples_for 'a permission set' do
2
+ it 'has unique values' do
3
+ values = %w(one two two three)
4
+
5
+ permission_set = described_class.new(values)
6
+ permission_set.to_a.should == values.uniq
7
+ end
8
+
9
+ it 'initializes with no values' do
10
+ lambda{ described_class.new }.should_not raise_error
11
+ described_class.new.should be_empty
12
+ end
13
+
14
+ it 'stringifies values when initializing' do
15
+ values = [:one, :two, :three]
16
+
17
+ permission_set = described_class.new(values)
18
+ permission_set.to_a.should == values.map(&:to_s)
19
+ end
20
+
21
+ it 'stringifies values when adding' do
22
+ values = [:one, :two, :three]
23
+
24
+ permission_set = described_class.new + values
25
+ permission_set.to_a.should == values.map(&:to_s)
26
+ end
27
+
28
+ it 'stringifies a value when checking for inclusion' do
29
+ values = %w(one two three)
30
+ value = values.first.to_sym
31
+
32
+ permission_set = described_class.new(values)
33
+ permission_set.should include(value)
34
+ end
35
+ end
@@ -0,0 +1,66 @@
1
+ require 'active_support/core_ext/hash/slice'
2
+
3
+ shared_examples_for 'a sanitizer' do
4
+ subject{ described_class.new }
5
+
6
+ let(:logger){ FakeLogger.new }
7
+ let(:params){ {:one => 1, :two => 2} }
8
+
9
+ it 'has its own logger' do
10
+ logger.should_receive(:debug).once
11
+
12
+ subject.logger = logger
13
+ subject.logger.should == logger
14
+
15
+ subject.logger.debug
16
+ end
17
+
18
+ it 'removes denied keys from a hash' do
19
+ subject.stub(:deny?).with(:one).and_return(false)
20
+ subject.stub(:deny?).with(:two).and_return(true)
21
+ subject.sanitize(params).should == params.slice(:one)
22
+ end
23
+
24
+ it "doesn't remove keys if none are denied" do
25
+ subject.stub(:deny? => false)
26
+ subject.sanitize(params).should == params
27
+ end
28
+
29
+ it 'debugs removed keys' do
30
+ subject.logger = logger
31
+ subject.logger.should_receive(:debug).once
32
+
33
+ subject.stub(:deny? => true)
34
+ subject.sanitize(params)
35
+ end
36
+
37
+ it "doesn't debug if there's no logger" do
38
+ subject.logger.should be_nil
39
+
40
+ allow_message_expectations_on_nil
41
+ subject.logger.should_receive(:debug).never
42
+
43
+ subject.stub(:deny? => true)
44
+ subject.sanitize(params)
45
+ end
46
+
47
+ it "doesn't debug if no keys are removed" do
48
+ subject.logger = logger
49
+ subject.logger.should_receive(:debug).never
50
+
51
+ subject.stub(:deny? => false)
52
+ subject.sanitize(params)
53
+ end
54
+
55
+ it 'only debugs the removed keys' do
56
+ subject.logger = logger
57
+
58
+ subject.stub(:deny?).with(:one).and_return(false)
59
+ subject.stub(:deny?).with(:two).and_return(true)
60
+ subject.sanitize(params)
61
+
62
+ message = subject.logger.output.pop
63
+ message.should_not match(/\bone\b/)
64
+ message.should match(/\btwo\b/)
65
+ end
66
+ end
metadata ADDED
@@ -0,0 +1,148 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: periscope
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - Steve Richert
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-02-03 00:00:00 -05:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: activesupport
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 3
30
+ - 0
31
+ - 0
32
+ version: 3.0.0
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: rspec
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ segments:
44
+ - 0
45
+ version: "0"
46
+ type: :development
47
+ version_requirements: *id002
48
+ - !ruby/object:Gem::Dependency
49
+ name: sqlite3
50
+ prerelease: false
51
+ requirement: &id003 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ segments:
57
+ - 0
58
+ version: "0"
59
+ type: :development
60
+ version_requirements: *id003
61
+ - !ruby/object:Gem::Dependency
62
+ name: activerecord
63
+ prerelease: false
64
+ requirement: &id004 !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ segments:
70
+ - 3
71
+ - 0
72
+ - 0
73
+ version: 3.0.0
74
+ type: :development
75
+ version_requirements: *id004
76
+ description: Periscope acts like attr_accessible or attr_protected, but for your models' scopes.
77
+ email:
78
+ - steve.richert@gmail.com
79
+ executables: []
80
+
81
+ extensions: []
82
+
83
+ extra_rdoc_files: []
84
+
85
+ files:
86
+ - .gitignore
87
+ - Gemfile
88
+ - README.rdoc
89
+ - Rakefile
90
+ - init.rb
91
+ - lib/periscope.rb
92
+ - lib/periscope/adapters/abstract.rb
93
+ - lib/periscope/adapters/active_record.rb
94
+ - lib/periscope/permission_set.rb
95
+ - lib/periscope/sanitizer.rb
96
+ - lib/periscope/version.rb
97
+ - periscope.gemspec
98
+ - spec/periscope/adapters/active_record_spec.rb
99
+ - spec/periscope/black_list_spec.rb
100
+ - spec/periscope/version_spec.rb
101
+ - spec/periscope/white_list_spec.rb
102
+ - spec/spec_helper.rb
103
+ - spec/support/abstract_examples.rb
104
+ - spec/support/connections/active_record.rb
105
+ - spec/support/permission_set_examples.rb
106
+ - spec/support/sanitizer_examples.rb
107
+ has_rdoc: true
108
+ homepage: https://github.com/laserlemon/periscope
109
+ licenses: []
110
+
111
+ post_install_message:
112
+ rdoc_options: []
113
+
114
+ require_paths:
115
+ - lib
116
+ required_ruby_version: !ruby/object:Gem::Requirement
117
+ none: false
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ segments:
122
+ - 0
123
+ version: "0"
124
+ required_rubygems_version: !ruby/object:Gem::Requirement
125
+ none: false
126
+ requirements:
127
+ - - ">="
128
+ - !ruby/object:Gem::Version
129
+ segments:
130
+ - 0
131
+ version: "0"
132
+ requirements: []
133
+
134
+ rubyforge_project: periscope
135
+ rubygems_version: 1.3.7
136
+ signing_key:
137
+ specification_version: 3
138
+ summary: Bring your models' scopes up above the surface.
139
+ test_files:
140
+ - spec/periscope/adapters/active_record_spec.rb
141
+ - spec/periscope/black_list_spec.rb
142
+ - spec/periscope/version_spec.rb
143
+ - spec/periscope/white_list_spec.rb
144
+ - spec/spec_helper.rb
145
+ - spec/support/abstract_examples.rb
146
+ - spec/support/connections/active_record.rb
147
+ - spec/support/permission_set_examples.rb
148
+ - spec/support/sanitizer_examples.rb