scoped_from 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.
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ .DS_Store
2
+ .bundle
3
+ .rvmrc
4
+ pkg/*
5
+ spec/test.sqlite3
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --colour
2
+ --format progress
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,82 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ scoped_from (0.1)
5
+ activerecord (~> 3.0.0)
6
+
7
+ GEM
8
+ remote: http://rubygems.org/
9
+ specs:
10
+ abstract (1.0.0)
11
+ actionpack (3.0.5)
12
+ activemodel (= 3.0.5)
13
+ activesupport (= 3.0.5)
14
+ builder (~> 2.1.2)
15
+ erubis (~> 2.6.6)
16
+ i18n (~> 0.4)
17
+ rack (~> 1.2.1)
18
+ rack-mount (~> 0.6.13)
19
+ rack-test (~> 0.5.7)
20
+ tzinfo (~> 0.3.23)
21
+ activemodel (3.0.5)
22
+ activesupport (= 3.0.5)
23
+ builder (~> 2.1.2)
24
+ i18n (~> 0.4)
25
+ activerecord (3.0.5)
26
+ activemodel (= 3.0.5)
27
+ activesupport (= 3.0.5)
28
+ arel (~> 2.0.2)
29
+ tzinfo (~> 0.3.23)
30
+ activesupport (3.0.5)
31
+ arel (2.0.9)
32
+ builder (2.1.2)
33
+ columnize (0.3.2)
34
+ diff-lcs (1.1.2)
35
+ erubis (2.6.6)
36
+ abstract (>= 1.0.0)
37
+ i18n (0.5.0)
38
+ linecache (0.43)
39
+ rack (1.2.2)
40
+ rack-mount (0.6.13)
41
+ rack (>= 1.0.0)
42
+ rack-test (0.5.7)
43
+ rack (>= 1.0)
44
+ railties (3.0.5)
45
+ actionpack (= 3.0.5)
46
+ activesupport (= 3.0.5)
47
+ rake (>= 0.8.7)
48
+ thor (~> 0.14.4)
49
+ rake (0.8.7)
50
+ rspec (2.5.0)
51
+ rspec-core (~> 2.5.0)
52
+ rspec-expectations (~> 2.5.0)
53
+ rspec-mocks (~> 2.5.0)
54
+ rspec-core (2.5.1)
55
+ rspec-expectations (2.5.0)
56
+ diff-lcs (~> 1.1.2)
57
+ rspec-mocks (2.5.0)
58
+ rspec-rails (2.5.0)
59
+ actionpack (~> 3.0)
60
+ activesupport (~> 3.0)
61
+ railties (~> 3.0)
62
+ rspec (~> 2.5.0)
63
+ ruby-debug (0.10.4)
64
+ columnize (>= 0.1)
65
+ ruby-debug-base (~> 0.10.4.0)
66
+ ruby-debug-base (0.10.4)
67
+ linecache (>= 0.3)
68
+ sqlite3 (1.3.3)
69
+ sqlite3-ruby (1.3.3)
70
+ sqlite3 (>= 1.3.3)
71
+ thor (0.14.6)
72
+ tzinfo (0.3.25)
73
+
74
+ PLATFORMS
75
+ ruby
76
+
77
+ DEPENDENCIES
78
+ rspec (~> 2.5.0)
79
+ rspec-rails (~> 2.5.0)
80
+ ruby-debug
81
+ scoped_from!
82
+ sqlite3-ruby
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Alexis Toulotte
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.mdown ADDED
@@ -0,0 +1,135 @@
1
+ # ScopedFrom
2
+
3
+ Provides a simple mapping between scopes and controller parameters for
4
+ [Ruby On Rails 3](http://rubyonrails.org/).
5
+
6
+ Let see a simple example:
7
+
8
+ First, a model with some scopes:
9
+
10
+ class Post < ActiveRecord::Base
11
+
12
+ scope :commented, where('comments_count > 0')
13
+
14
+ scope :created_between, lambda { |after, before|
15
+ where('created_at >= ? AND created_at <= ?', after, before)
16
+ }
17
+
18
+ scope :search, lambda { |pattern|
19
+ where('body LIKE ?', "%#{pattern}%")
20
+ }
21
+
22
+ scope :with_category, lambda { |category_id|
23
+ where(:category_id, category_id)
24
+ }
25
+
26
+ end
27
+
28
+ After, a controller:
29
+
30
+ class PostsController < ActionController::Base
31
+
32
+ def index
33
+ @posts = Post.scoped_from(params)
34
+ end
35
+
36
+ end
37
+
38
+ Then, it just filter your model from params:
39
+
40
+ /posts?commented=1
41
+ /posts?search=rails
42
+ /posts?search=rails&commented=1&with_category=42
43
+
44
+ ## Accepted scopes
45
+
46
+ All scopes can be mapped with `scoped_from` method **except** scopes taking a
47
+ `lambda` (or a `Proc`) with an arity greater than 1 (for example:
48
+ `created_between` in the above code).
49
+
50
+ Scopes with no argument are invoked if parameter value is evaluated as `true`.
51
+ It includes `"true"`, `"yes"`, `"y"`, `"on"`, and `"1"` strings.
52
+
53
+ ## Scopes restriction
54
+
55
+ You can restrict mapping to some scopes with `:only` option:
56
+
57
+ @posts = Post.scoped_from(params, :only => ['commented', 'search'])
58
+
59
+ You can also exclude some scopes from mapping with `:except` option:
60
+
61
+ @posts = Post.scoped_from(params, :except => 'commented')
62
+
63
+ ## Mapping order
64
+
65
+ If you need to map an SQL order, just pass `order` parameter:
66
+
67
+ @posts = Post.scoped_from(:order => 'created_at')
68
+
69
+ Order direction can be specified using a dot, space or `:` as delimiter:
70
+
71
+ @posts = Post.scoped_from(:order => 'created_at.desc')
72
+
73
+ Note that order is SQL safe with `scoped_from` method (columns names are
74
+ checked).
75
+
76
+ ## Some cool stuff
77
+
78
+ If your provide an array as parameter value, scope is invoked with each item
79
+ of the array:
80
+
81
+ @posts = Post.scoped_from(:search => ['bar', 'foo'])
82
+
83
+ is equivalent to
84
+
85
+ @posts = Post.search('bar').search('foo')
86
+
87
+ By default, blank parameter values are ignored, you can include them with
88
+ `:include_blank` option:
89
+
90
+ @posts = Post.scoped_from(params, :include_blank => true)
91
+
92
+ You may also want to filter on columns, just specify `:include_columns` option:
93
+
94
+ @posts = Post.scoped_from(params, :include_columns => true)
95
+
96
+ A query string can also be given to `scoped_from` method:
97
+
98
+ @posts = Post.scoped_from('with_category=24&search[]=foo&search[]=bar')
99
+
100
+ Returned scope from `scoped_from` method gives access to an internal query
101
+ object:
102
+
103
+ @posts = Post.scoped_from(params)
104
+ @query = @posts.query
105
+
106
+ This query provides you some convenience methods like `params`, `order_column`
107
+ and `order_direction`. This object can also be used to save user's search into
108
+ a database or other storage system.
109
+
110
+ But, you may also have to subclass this query class. You have to create a
111
+ subclass of `ScopedFrom::Query` named `#{RecordClassName}Query`. Here is an
112
+ example:
113
+
114
+ class PostQuery < ScopedFrom::Query
115
+
116
+ def category
117
+ Category.find_by_id(params[:with_category]) if params[:with_category]
118
+ end
119
+
120
+ end
121
+
122
+ This class has to be in load path.
123
+
124
+ Then into a view:
125
+
126
+ <% if @query.category %>
127
+ <p>All posts of category <%= @query.category.name %></p>
128
+ <% else %>
129
+ <p>All posts</p>
130
+ <% end %>
131
+
132
+ ## Executing test suite
133
+
134
+ This project is fully tested with [Rspec 2](http://github.com/rspec/rspec).
135
+ Just run `rake` (after a `bundle install`).
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require 'bundler'
2
+ require 'rspec/core/rake_task'
3
+
4
+ Bundler::GemHelper.install_tasks
5
+
6
+ desc 'Default: runs specs.'
7
+ task :default => :spec
8
+
9
+ desc 'Run all specs in spec directory.'
10
+ RSpec::Core::RakeTask.new(:spec)
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/lib/scoped_from')
@@ -0,0 +1,40 @@
1
+ module ScopedFrom
2
+
3
+ module ActiveRecord
4
+
5
+ def self.included(base)
6
+ base.extend(ClassMethods)
7
+ end
8
+
9
+ module ClassMethods
10
+
11
+ def scope(name, scope_options, &block)
12
+ super
13
+ scope_arities[name] = scope_options.is_a?(Proc) ? scope_options.arity : -1
14
+ end
15
+
16
+ def scope_with_one_argument?(name)
17
+ scope_arities[name] == 1
18
+ end
19
+
20
+ def scope_without_argument?(name)
21
+ [-1, 0].include?(scope_arities[name])
22
+ end
23
+
24
+ def scoped_from(params, options = {})
25
+ query_class = "#{name}Query".constantize rescue nil
26
+ query_class = Query unless query_class.is_a?(Class) && query_class.ancestors.include?(Query)
27
+ query_class.new(self, params, options).scope
28
+ end
29
+
30
+ private
31
+
32
+ def scope_arities
33
+ read_inheritable_attribute(:scope_arities) || write_inheritable_attribute(:scope_arities, ActiveSupport::HashWithIndifferentAccess.new)
34
+ end
35
+
36
+ end
37
+
38
+ end
39
+
40
+ end
@@ -0,0 +1,103 @@
1
+ module ScopedFrom
2
+
3
+ class Query
4
+
5
+ ORDER_DIRECTIONS = %w( asc desc ).freeze
6
+ TRUE_VALUES = %w( true yes y on 1 ).freeze
7
+
8
+ attr_reader :params
9
+
10
+ # Available options are: - :only : to restrict to specified keys.
11
+ # - :except : to ignore specified keys.
12
+ # - :include_blank : to include blank values
13
+ # (default false).
14
+ def initialize(scope, params, options = {})
15
+ @scope = scope.scoped
16
+ @options = options
17
+ self.params = params
18
+ end
19
+
20
+ def order_column
21
+ parse_order(params['order'])[:column]
22
+ end
23
+
24
+ def order_direction
25
+ parse_order(params['order'])[:direction]
26
+ end
27
+
28
+ def scope
29
+ scope = @scope
30
+ params.each do |name, value|
31
+ [value].flatten.each do |value|
32
+ scope = scoped(scope, name, value)
33
+ end
34
+ end
35
+ decorate_scope(scope)
36
+ end
37
+
38
+ protected
39
+
40
+ def decorate_scope(scope)
41
+ return scope if scope.respond_to?(:query)
42
+ def scope.query
43
+ @__query
44
+ end
45
+ scope.instance_variable_set('@__query', self)
46
+ scope
47
+ end
48
+
49
+ def order_to_sql(value)
50
+ order = parse_order(value)
51
+ "#{order[:column]} #{order[:direction].upcase}" if order.present?
52
+ end
53
+
54
+ def scoped(scope, name, value)
55
+ if name.to_s == 'order'
56
+ scope.order(order_to_sql(value))
57
+ elsif scope.scope_with_one_argument?(name)
58
+ scope.send(name, value)
59
+ elsif scope.scope_without_argument?(name)
60
+ scope.send(name)
61
+ elsif scope.column_names.include?(name.to_s)
62
+ scope.scoped(:conditions => { name => value })
63
+ else
64
+ scope
65
+ end
66
+ end
67
+
68
+ def params=(params)
69
+ params = params.params if params.is_a?(self.class)
70
+ params = CGI.parse(params.to_s) unless params.is_a?(Hash)
71
+ @params = ActiveSupport::HashWithIndifferentAccess.new
72
+ params.each do |name, value|
73
+ values = [value].flatten
74
+ values.delete_if(&:blank?) unless @options[:include_blank]
75
+ next if values.empty?
76
+ if name.to_s == 'order'
77
+ order = parse_order(values.last)
78
+ @params[name] = "#{order[:column]}.#{order[:direction]}" if order.present?
79
+ elsif @scope.scope_without_argument?(name)
80
+ @params[name] = true if values.any? { |value| true?(value) }
81
+ elsif @scope.scope_with_one_argument?(name) || @options[:include_columns].present? && @scope.column_names.include?(name.to_s)
82
+ value = values.many? ? values : values.first
83
+ @params[name] = @params[name] ? [@params[name], value].flatten : value
84
+ end
85
+ end
86
+ @params.slice!(*[@options[:only]].flatten) if @options[:only].present?
87
+ @params.except!(*[@options[:except]].flatten) if @options[:except].present?
88
+ end
89
+
90
+ def parse_order(value)
91
+ column, direction = value.to_s.split(/[\.:\s]+/, 2)
92
+ direction = direction.to_s.downcase
93
+ direction = ORDER_DIRECTIONS.first unless ORDER_DIRECTIONS.include?(direction)
94
+ @scope.column_names.include?(column) ? { :column => column, :direction => direction } : {}
95
+ end
96
+
97
+ def true?(value)
98
+ TRUE_VALUES.include?(value.to_s.strip.downcase)
99
+ end
100
+
101
+ end
102
+
103
+ end
@@ -0,0 +1,24 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'cgi'
4
+ require 'active_record'
5
+ require 'active_support/core_ext/object/to_query'
6
+
7
+ module ScopedFrom
8
+
9
+ class << self
10
+
11
+ def version
12
+ @@version ||= File.read(File.expand_path(File.dirname(__FILE__) + '/../VERSION')).strip.freeze
13
+ end
14
+
15
+ end
16
+
17
+ end
18
+
19
+ lib_path = File.expand_path(File.dirname(__FILE__) + '/scoped_from')
20
+
21
+ require "#{lib_path}/active_record"
22
+ require "#{lib_path}/query"
23
+
24
+ ActiveRecord::Base.send(:include, ScopedFrom::ActiveRecord)
@@ -0,0 +1,24 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'scoped_from'
3
+ s.version = File.read(File.expand_path(File.dirname(__FILE__) + '/VERSION')).strip
4
+ s.platform = Gem::Platform::RUBY
5
+ s.author = 'Alexis Toulotte'
6
+ s.email = 'al@alweb.org'
7
+ s.homepage = 'https://github.com/alexistoulotte/scoped_from'
8
+ s.summary = 'Mapping between scopes and parameters for Rails'
9
+ s.description = 'Provides a simple mapping between Active Record scopes and controller parameters for Ruby On Rails 3'
10
+
11
+ s.rubyforge_project = 'scoped_from'
12
+
13
+ s.files = `git ls-files`.split("\n")
14
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
15
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
16
+ s.require_paths = ['lib']
17
+
18
+ s.add_dependency 'activerecord', '~> 3.0.0'
19
+
20
+ s.add_development_dependency 'rspec', '~> 2.5.0'
21
+ s.add_development_dependency 'rspec-rails', '~> 2.5.0'
22
+ s.add_development_dependency 'ruby-debug'
23
+ s.add_development_dependency 'sqlite3-ruby'
24
+ end
@@ -0,0 +1,2 @@
1
+ class Comment < ActiveRecord::Base
2
+ end
@@ -0,0 +1,2 @@
1
+ class CommentQuery < ScopedFrom::Query
2
+ end
@@ -0,0 +1,2 @@
1
+ class Post < ActiveRecord::Base
2
+ end
@@ -0,0 +1,14 @@
1
+ class User < ActiveRecord::Base
2
+
3
+ scope :enabled, :conditions => { :enabled => true }
4
+ scope :search, lambda { |pattern|
5
+ where('firstname LIKE ? OR lastname LIKE ?', "%#{pattern}%", "%#{pattern}%")
6
+ }
7
+ scope :created_between, lambda { |after, before|
8
+ where('created_at >= ? AND created_at <= ?', after, before)
9
+ }
10
+ scope :latest, lambda {
11
+ where('created_at >= ?', 1.week.ago)
12
+ }
13
+
14
+ end
@@ -0,0 +1,2 @@
1
+ class UserQuery < String
2
+ end
@@ -0,0 +1,2 @@
1
+ class Vote < ActiveRecord::Base
2
+ end
@@ -0,0 +1,2 @@
1
+ module VoteQuery
2
+ end
@@ -0,0 +1,121 @@
1
+ require 'spec_helper'
2
+
3
+ describe ScopedFrom::ActiveRecord do
4
+
5
+ describe '#scope_with_one_argument?' do
6
+
7
+ it 'is true if scope has one argument' do
8
+ User.should be_scope_with_one_argument(:search)
9
+ User.should be_scope_with_one_argument('search')
10
+ end
11
+
12
+ it 'is false if scope has no argument' do
13
+ User.should_not be_scope_with_one_argument(:latest)
14
+ User.should_not be_scope_with_one_argument('latest')
15
+ end
16
+
17
+ it 'is false if scope has more than one argument' do
18
+ User.should_not be_scope_with_one_argument(:created_between)
19
+ User.should_not be_scope_with_one_argument('created_between')
20
+ end
21
+
22
+ it 'is false if scope is not a proc' do
23
+ User.should_not be_scope_with_one_argument(:enabled)
24
+ User.should_not be_scope_with_one_argument('enabled')
25
+ end
26
+
27
+ it 'is false if scope does not exist' do
28
+ User.should_not be_scope_with_one_argument(:foo)
29
+ User.should_not be_scope_with_one_argument('foo')
30
+ end
31
+
32
+ end
33
+
34
+ describe 'scope_without_argument?' do
35
+
36
+ it 'is true if scope has no argument' do
37
+ User.should be_scope_without_argument(:latest)
38
+ User.should be_scope_without_argument('latest')
39
+ end
40
+
41
+ it 'is true if scope is not a proc' do
42
+ User.should be_scope_without_argument(:enabled)
43
+ User.should be_scope_without_argument('enabled')
44
+ end
45
+
46
+ it 'is false if scope has one argument' do
47
+ User.should_not be_scope_without_argument(:search)
48
+ User.should_not be_scope_without_argument('search')
49
+ end
50
+
51
+ it 'is false if scope has more than one argument' do
52
+ User.should_not be_scope_without_argument(:created_between)
53
+ User.should_not be_scope_without_argument('created_between')
54
+ end
55
+
56
+ it 'is false if scope does not exist' do
57
+ User.should_not be_scope_without_argument(:foo)
58
+ User.should_not be_scope_without_argument('foo')
59
+ end
60
+
61
+ end
62
+
63
+ describe '#scoped_from' do
64
+
65
+ it 'just build a new query and return its scope' do
66
+ query = mock(:query)
67
+ query.should_receive(:scope).and_return(42)
68
+ ScopedFrom::Query.should_receive(:new).with(User, 'foo', :except => 'bam').and_return(query)
69
+ User.scoped_from('foo', :except => 'bam').should == 42
70
+ end
71
+
72
+ it 'build scopes' do
73
+ User.scoped_from(:search => 'jane').should == [users(:jane)]
74
+ User.scoped_from(:search => 'john').should == [users(:john)]
75
+ end
76
+
77
+ it 'can be chained with other scopes' do
78
+ User.scoped_from(:search => 'jane').should == [users(:jane)]
79
+ User.enabled.scoped_from(:search => 'jane').should == []
80
+ end
81
+
82
+ it 'can be used with order as parameter' do
83
+ User.scoped_from(:order => 'firstname').first.should == users(:jane)
84
+ User.scoped_from(:order => 'firstname.desc').first.should == users(:john)
85
+ end
86
+
87
+ it 'builds a ScopedFrom::Query' do
88
+ User.scoped_from({}).query.class.should be(ScopedFrom::Query)
89
+ end
90
+
91
+ it 'builds a ScopedFrom::Query if #{RecordClassName}Query is not defined' do
92
+ Post.scoped_from({}).query.class.should be(ScopedFrom::Query)
93
+ Object.const_defined?('PostQuery').should be_false
94
+ expect {
95
+ PostQuery
96
+ }.to raise_error(NameError, 'uninitialized constant PostQuery')
97
+ end
98
+
99
+ it 'builds a #{Class}Query if #{RecordClassName}Query is defined and is a ScopedFrom::Query' do
100
+ Comment.scoped_from({}).query.class.should be(CommentQuery)
101
+ Comment.where(:foo => 'bar').scoped_from({}).query.class.should be(CommentQuery)
102
+ CommentQuery.should be_a(Class)
103
+ CommentQuery.ancestors.should include(ScopedFrom::Query)
104
+ end
105
+
106
+ it 'builds a ScopedFrom::Query if #{RecordClassName}Query is defined but not a subclass of ScopedFrom::Query' do
107
+ User.scoped_from({}).query.class.should be(ScopedFrom::Query)
108
+ Object.const_defined?('UserQuery').should be_true
109
+ UserQuery.should be_a(Class)
110
+ UserQuery.ancestors.should_not include(ScopedFrom::Query)
111
+ end
112
+
113
+ it 'builds a ScopedFrom::Query if #{RecordClassName}Query is defined but is a module' do
114
+ Vote.scoped_from({}).query.class.should be(ScopedFrom::Query)
115
+ Object.const_defined?('VoteQuery').should be_true
116
+ VoteQuery.should be_a(Module)
117
+ end
118
+
119
+ end
120
+
121
+ end
@@ -0,0 +1,426 @@
1
+ require 'spec_helper'
2
+
3
+ describe ScopedFrom::Query do
4
+
5
+ def query(scope = User, params = {}, options = {})
6
+ ScopedFrom::Query.new(scope, params, options)
7
+ end
8
+
9
+ describe '#initialize' do
10
+
11
+ it 'invokes #scoped method on specified scope' do
12
+ User.should_receive(:scoped)
13
+ ScopedFrom::Query.new(User, {})
14
+ end
15
+
16
+ end
17
+
18
+ describe '#order_column' do
19
+
20
+ it 'is column specified into "order" parameter' do
21
+ query(User, :order => 'firstname').order_column.should == 'firstname'
22
+ query(User, :order => 'lastname.desc').order_column.should == 'lastname'
23
+ end
24
+
25
+ it 'is nil if column does not exist' do
26
+ query(User, :order => 'foo').order_column.should be_nil
27
+ end
28
+
29
+ it 'is nil if "order" param is not specified' do
30
+ query(User, :search => 'foo').order_column.should be_nil
31
+ end
32
+
33
+ end
34
+
35
+ describe '#order_direction' do
36
+
37
+ it 'is direction specified into "order" parameter' do
38
+ query(User, :order => 'firstname.asc').order_direction.should == 'asc'
39
+ query(User, :order => 'firstname.desc').order_direction.should == 'desc'
40
+ end
41
+
42
+ it 'is "asc" if direction is not specified' do
43
+ query(User, :order => 'firstname').order_direction.should == 'asc'
44
+ end
45
+
46
+ it 'is "asc" if direction is invalid' do
47
+ query(User, :order => 'firstname.foo').order_direction.should == 'asc'
48
+ end
49
+
50
+ it 'is direction even specified in another case' do
51
+ query(User, :order => 'firstname.ASc').order_direction.should == 'asc'
52
+ query(User, :order => 'firstname.DeSC').order_direction.should == 'desc'
53
+ end
54
+
55
+ it 'is nil if column does not exist' do
56
+ query(User, :order => 'foo.desc').order_direction.should be_nil
57
+ end
58
+
59
+ it 'is nil if "order" param is not specified' do
60
+ query(User, :search => 'foo').order_direction.should be_nil
61
+ end
62
+
63
+ end
64
+
65
+ describe '#params' do
66
+
67
+ it 'returns params specified at initialization' do
68
+ query(User, :search => 'foo', 'enabled' => true).params.should == { 'search' => 'foo', 'enabled' => true }
69
+ end
70
+
71
+ it 'returns an hash with indifferent access' do
72
+ query(User, 'search' => 'bar').params.should be_a(ActiveSupport::HashWithIndifferentAccess)
73
+ query(User, 'search' => 'bar').params[:search].should == 'bar'
74
+ query(User, :search => 'bar').params['search'].should == 'bar'
75
+ end
76
+
77
+ it 'can be converted to query string' do
78
+ query(User, :search => ['foo', 'bar'], 'enabled' => '1').params.to_query.should == 'enabled=true&search[]=foo&search[]=bar'
79
+ end
80
+
81
+ end
82
+
83
+ describe '#params=' do
84
+
85
+ it 'does not fails if nil is given' do
86
+ query(User, nil).params.should == {}
87
+ end
88
+
89
+ it 'removes values that are not scopes' do
90
+ query(User, :foo => 'bar', 'search' => 'foo', :enabled => true).params.should == { 'search' => 'foo', 'enabled' => true }
91
+ end
92
+
93
+ it 'removes blank values' do
94
+ query(User, 'enabled' => true, 'search' => " \n").params.should == { 'enabled' => true }
95
+ end
96
+
97
+ it 'is case sensitive' do
98
+ query(User, 'Enabled' => true, "SEARCH" => 'bar').params.should be_empty
99
+ end
100
+
101
+ it 'parse query string' do
102
+ query(User, 'search=foo%26baz&latest=true').params.should == { 'search' => 'foo&baz', 'latest' => true }
103
+ end
104
+
105
+ it 'removes blank values from query string' do
106
+ query(User, 'search=baz&toto=&bar=%20').params.should == { 'search' => 'baz' }
107
+ end
108
+
109
+ it 'unescapes UTF-8 chars' do
110
+ query(User, 'search=%C3%A9').params.should == { 'search' => 'é' }
111
+ end
112
+
113
+ it 'can have multiple values (from hash)' do
114
+ query(User, :search => ['bar', 'baz']).params.should == { 'search' => ['bar', 'baz'] }
115
+ end
116
+
117
+ it 'can have multiple values (from query string)' do
118
+ query(User, 'search=bar&search=baz').params.should == { 'search' => ['bar', 'baz'] }
119
+ end
120
+
121
+ it 'removes blank values from array' do
122
+ query(User, :search => [nil, 'bar', "\n ", 'baz']).params.should == { 'search' => ['bar', 'baz'] }
123
+ end
124
+
125
+ it 'converts value to true (or remove it) if scope takes no argument' do
126
+ query(User, :latest => 'y').params.should == { 'latest' => true }
127
+ query(User, :latest => 'no').params.should == {}
128
+ end
129
+
130
+ it 'converts array value to true (or remove it) if scope takes no argument' do
131
+ query(User, :latest => ['no', 'yes']).params.should == { 'latest' => true }
132
+ query(User, :latest => ['no', nil]).params.should == {}
133
+ query(User, :latest => ['fo']).params.should == {}
134
+ end
135
+
136
+ it 'flats array' do
137
+ query(User, :search => [nil, ['bar', '', 'foo', ["\n ", 'baz']]]).params.should == { 'search' => ['bar', 'foo', 'baz'] }
138
+ end
139
+
140
+ it 'change array with a single value in one value' do
141
+ query(User, :search => [nil, 'bar', "\n"]).params.should == { 'search' => 'bar' }
142
+ end
143
+
144
+ it 'does not modify given hash' do
145
+ hash = { :search => 'foo', :enabled => '1', :bar => 'foo' }
146
+ query(User, hash)
147
+ hash.should == { :search => 'foo', :enabled => '1', :bar => 'foo' }
148
+ end
149
+
150
+ it 'does not modify given array' do
151
+ items = ['bar', 'foo', nil]
152
+ query(User, :search => items)
153
+ items.should == ['bar', 'foo', nil]
154
+ end
155
+
156
+ it 'accepts :only option' do
157
+ query(User, { :search => 'bar', :enabled => 'true' }, :only => [:search]).params.should == { 'search' => 'bar' }
158
+ query(User, { :search => 'bar', :enabled => 'true' }, :only => 'search').params.should == { 'search' => 'bar' }
159
+ end
160
+
161
+ it 'accepts :except option' do
162
+ query(User, { :search => 'bar', :enabled => true }, :except => [:search]).params.should == { 'enabled' => true }
163
+ query(User, { :search => 'bar', :enabled => true }, :except => 'search').params.should == { 'enabled' => true }
164
+ end
165
+
166
+ it 'accepts a query instance' do
167
+ query(User, query(User, :search => 'toto')).params.should == { 'search' => 'toto' }
168
+ end
169
+
170
+ it 'preserve blank values if :include_blank option is true' do
171
+ query(User, { :search => "\n ", 'enabled' => true }, :include_blank => true).params.should == { 'search' => "\n ", 'enabled' => true }
172
+ end
173
+
174
+ it 'preserve blank values from array if :include_blank option is true' do
175
+ query(User, { 'search' => ["\n ", 'toto', 'titi'] }, :include_blank => true).params.should == { 'search' => ["\n ", 'toto', 'titi'] }
176
+ query(User, { 'search' => [] }, :include_blank => true).params.should == {}
177
+ end
178
+
179
+ it 'also preserve blank on query string if :include_blank option is true' do
180
+ query(User, 'search=%20&enabled=true&search=foo', :include_blank => true).params.should == { 'search' => [' ', 'foo'], 'enabled' => true }
181
+ end
182
+
183
+ it 'removes column values' do
184
+ query(User, 'firstname' => 'Jane', 'foo' => 'bar').params.should == {}
185
+ end
186
+
187
+ it 'include column values if :include_columns option is specified' do
188
+ query(User, { 'firstname' => 'Jane', 'foo' => 'bar' }, :include_columns => true).params.should == { 'firstname' => 'Jane' }
189
+ query(User, { :firstname => 'Jane', :foo => 'bar' }, :include_columns => true).params.should == { 'firstname' => 'Jane' }
190
+ query(User, { 'firstname' => ['Jane', 'John'], 'foo' => 'bar' }, :include_columns => true).params.should == { 'firstname' => ['Jane', 'John'] }
191
+ query(User, { 'firstname' => "\n ", 'foo' => 'bar' }, :include_columns => true).params.should == {}
192
+ query(User, { 'firstname' => "\n ", 'foo' => 'bar' }, :include_columns => true, :include_blank => true).params.should == { 'firstname' => "\n " }
193
+ end
194
+
195
+ it 'maps an "order"' do
196
+ query(User, { 'order' => 'firstname.asc' }).params.should == { 'order' => 'firstname.asc' }
197
+ end
198
+
199
+ it 'does not map "order" if column is invalid' do
200
+ query(User, { 'order' => 'foo.asc' }).params.should == {}
201
+ end
202
+
203
+ it 'use "asc" order direction by default' do
204
+ query(User, { 'order' => 'firstname' }).params.should == { 'order' => 'firstname.asc' }
205
+ end
206
+
207
+ it 'use "asc" order direction if invalid' do
208
+ query(User, { 'order' => 'firstname.bar' }).params.should == { 'order' => 'firstname.asc' }
209
+ end
210
+
211
+ it 'use "desc" order direction if specified' do
212
+ query(User, { 'order' => 'firstname.desc' }).params.should == { 'order' => 'firstname.desc' }
213
+ end
214
+
215
+ it 'order direction is case insensitive' do
216
+ query(User, { 'order' => 'firstname.Asc' }).params.should == { 'order' => 'firstname.asc' }
217
+ query(User, { 'order' => 'firstname.DESC' }).params.should == { 'order' => 'firstname.desc' }
218
+ end
219
+
220
+ it 'order can be specified as symbol' do
221
+ query(User, { :order => 'firstname.desc' }).params.should == { 'order' => 'firstname.desc' }
222
+ end
223
+
224
+ it "order is case sensitive" do
225
+ query(User, { 'Order' => 'firstname.desc' }).params.should == {}
226
+ end
227
+
228
+ it 'use last "order" if many are specified' do
229
+ query(User, { 'order' => ['firstname.Asc', 'lastname.DESC'] }).params.should == { 'order' => 'lastname.desc' }
230
+ query(User, { 'order' => ['firstname.Asc', 'lastname.DESC', 'firstname.desc'] }).params.should == { 'order' => 'firstname.desc' }
231
+ end
232
+
233
+ it 'order can be delimited by a space' do
234
+ query(User, { 'order' => 'firstname ASC' }).params.should == { 'order' => 'firstname.asc' }
235
+ end
236
+
237
+ it 'order can be delimited by any white space' do
238
+ query(User, { 'order' => "firstname\nASC" }).params.should == { 'order' => 'firstname.asc' }
239
+ query(User, { 'order' => "firstname\t ASC" }).params.should == { 'order' => 'firstname.asc' }
240
+ end
241
+
242
+ it 'order can be delimited by a ":"' do
243
+ query(User, { 'order' => "firstname:ASC" }).params.should == { 'order' => 'firstname.asc' }
244
+ end
245
+
246
+ it 'order can be delimited by more than one delimiter' do
247
+ query(User, { 'order' => "firstname :. ASC" }).params.should == { 'order' => 'firstname.asc' }
248
+ end
249
+
250
+ end
251
+
252
+ describe '#scope' do
253
+
254
+ it 'does not execute any query' do
255
+ User.should_not_receive(:connection)
256
+ query(User, :enabled => true).scope
257
+ end
258
+
259
+ it 'works with scopes with a lambda without arguments' do
260
+ users(:jane).update_attribute(:created_at, 10.days.ago)
261
+ query(User, :latest => true).scope.should == [users(:john)]
262
+ query(User, :latest => false).scope.should == [users(:john), users(:jane)]
263
+ end
264
+
265
+ it 'does not modify scope specified at initialization' do
266
+ scope = User.search('foo')
267
+ q = query(scope, :enabled => true)
268
+ expect {
269
+ expect {
270
+ q.scope
271
+ }.to_not change { q.instance_variable_get('@scope') }
272
+ }.to_not change { scope }
273
+ end
274
+
275
+ it 'returns scope (#scoped) specified at construction if params are empty' do
276
+ query.scope.should_not == User
277
+ query.scope.should == User.scoped
278
+ end
279
+
280
+ it 'invokes many times scope if an array is given' do
281
+ query(User, :search => ['John', 'Doe']).scope.should == [users(:john)]
282
+ query(User, :search => ['John', 'Done']).scope.should == []
283
+ query(User, :search => ['John', 'Doe']).params.should == { 'search' => ['John', 'Doe'] }
284
+ end
285
+
286
+ it 'invokes many times scope if given twice (as string & symbol)' do
287
+ query(User, :search => 'John', 'search' => 'Done').params['search'].size.should be(2)
288
+ query(User, :search => 'John', 'search' => 'Done').params['search'].should include('John', 'Done')
289
+
290
+
291
+ query(User, :search => 'John', 'search' => ['Did', 'Done']).params['search'].size.should be(3)
292
+ query(User, :search => 'John', 'search' => ['Did', 'Done']).params['search'].should include('John', 'Did', 'Done')
293
+ end
294
+
295
+ it 'invokes last order if an array is given' do
296
+ query(User, :order => ['lastname', 'firstname']).scope.should == [users(:jane), users(:john)]
297
+ query(User, :order => ['lastname', 'firstname.desc']).scope.should == [users(:john), users(:jane)]
298
+ query(User, :order => ['firstname.desc', 'lastname']).scope.order_values.should == ['lastname ASC']
299
+ end
300
+
301
+ it 'defines #query method on returned scoped' do
302
+ query(User).scope.should respond_to(:query)
303
+ end
304
+
305
+ it 'does not defines #query method on scope if already defined' do
306
+ class User
307
+ def self.query
308
+ 42
309
+ end
310
+ end
311
+ begin
312
+ User.query.should be(42)
313
+ query(User).scope.query.should be(42)
314
+ ensure
315
+ class User
316
+ class << self
317
+ remove_method :query
318
+ end
319
+ end
320
+ end
321
+ end
322
+
323
+ it 'does not define #query method for future scopes' do
324
+ query(User).scope.query.should be_present
325
+ User.should_not respond_to(:query)
326
+ User.scoped.should_not respond_to(:query)
327
+ User.enabled.should_not respond_to(:query)
328
+ end
329
+
330
+ it 'defined #query method returns query' do
331
+ q = query(User)
332
+ q.scope.query.should be_a(ScopedFrom::Query)
333
+ q.scope.query.should be(q)
334
+ end
335
+
336
+ end
337
+
338
+ describe '#scoped' do
339
+
340
+ it 'returns given scope if it has no scope with specified name' do
341
+ query.send(:scoped, User, :foo, true).should == User
342
+ end
343
+
344
+ it 'returns given scope if scope takes more than 1 argument' do
345
+ query.send(:scoped, User, :created_between, true).should == User
346
+ end
347
+
348
+ it 'invokes scope without arguments if scope takes not argument' do
349
+ query.send(:scoped, User.scoped, :enabled, true).should == [users(:john)]
350
+ query.send(:scoped, User.scoped, :enabled, ' 1 ').should == [users(:john)]
351
+ query.send(:scoped, User.scoped, :enabled, 'off').should == [users(:john)]
352
+ end
353
+
354
+ it 'invokes scope with value has argument if scope takes one argument' do
355
+ query.send(:scoped, User.scoped, :search, 'doe').should == [users(:john), users(:jane)]
356
+ query.send(:scoped, User.scoped, :search, 'john').should == [users(:john)]
357
+ query.send(:scoped, User.scoped, :search, 'jane').should == [users(:jane)]
358
+ end
359
+
360
+ it 'scope on column conditions' do
361
+ query.send(:scoped, User.scoped, :firstname, 'Jane').should == [users(:jane)]
362
+ end
363
+
364
+ it 'invokes "order"' do
365
+ query.send(:scoped, User.scoped, :order, 'firstname.asc').should == [users(:jane), users(:john)]
366
+ query.send(:scoped, User.scoped, :order, 'firstname.desc').should == [users(:john), users(:jane)]
367
+ end
368
+
369
+ end
370
+
371
+ describe '#true?' do
372
+
373
+ it 'is true if true is given' do
374
+ query.send(:true?, true).should be_true
375
+ end
376
+
377
+ it 'is true if "true" is given' do
378
+ query.send(:true?, 'true').should be_true
379
+ query.send(:true?, 'True').should be_true
380
+ end
381
+
382
+ it 'is true if "1" is given' do
383
+ query.send(:true?, '1').should be_true
384
+ end
385
+
386
+ it 'is true if "on" is given' do
387
+ query.send(:true?, 'on').should be_true
388
+ query.send(:true?, 'ON ').should be_true
389
+ end
390
+
391
+ it 'is true if "yes" is given' do
392
+ query.send(:true?, 'yes').should be_true
393
+ query.send(:true?, ' Yes ').should be_true
394
+ end
395
+
396
+ it 'is true if "y" is given' do
397
+ query.send(:true?, 'y').should be_true
398
+ query.send(:true?, 'Y ').should be_true
399
+ end
400
+
401
+ it 'is false if false is given' do
402
+ query.send(:true?, false).should be_false
403
+ end
404
+
405
+ it 'is false if "false" is given' do
406
+ query.send(:true?, 'false').should be_false
407
+ query.send(:true?, 'FsALSE').should be_false
408
+ end
409
+
410
+ it 'is false if "0" is given' do
411
+ query.send(:true?, '0').should be_false
412
+ end
413
+
414
+ it 'is false if "off" is given' do
415
+ query.send(:true?, "off").should be_false
416
+ query.send(:true?, "Off").should be_false
417
+ end
418
+
419
+ it 'is false otherwise' do
420
+ query.send(:true?, 42).should be_false
421
+ query.send(:true?, 'bam').should be_false
422
+ end
423
+
424
+ end
425
+
426
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ describe ScopedFrom do
4
+
5
+ describe '.version' do
6
+
7
+ it 'is a string' do
8
+ ScopedFrom.version.should be_a(String)
9
+ end
10
+
11
+ it 'is with correct format' do
12
+ ScopedFrom.version.should match(/^\d+\.\d+(\.\d+)?/)
13
+ end
14
+
15
+ it 'is freezed' do
16
+ expect {
17
+ ScopedFrom.version.gsub!('.', '#')
18
+ }.to raise_error(TypeError, "can't modify frozen string")
19
+ end
20
+
21
+ end
22
+
23
+ end
@@ -0,0 +1,25 @@
1
+ ENV["RAILS_ENV"] ||= 'test'
2
+
3
+ require File.dirname(__FILE__) + '/../lib/scoped_from'
4
+
5
+ # Support
6
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
7
+
8
+ # Mocks
9
+ ActiveSupport::Dependencies.autoload_paths << "#{File.dirname(__FILE__)}/mocks"
10
+
11
+ RSpec.configure do |config|
12
+ config.mock_with(:rspec)
13
+
14
+ config.include(UserMacro)
15
+
16
+ config.before(:each) do
17
+ Comment.delete_all
18
+ Post.delete_all
19
+ User.delete_all
20
+ Vote.delete_all
21
+
22
+ create_user(:john, :firstname => 'John', :lastname => 'Doe', :enabled => true)
23
+ create_user(:jane, :firstname => 'Jane', :lastname => 'Doe', :enabled => false)
24
+ end
25
+ end
@@ -0,0 +1,11 @@
1
+ ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => "#{File.dirname(__FILE__)}/../../test.sqlite3", :timeout => 5000)
2
+
3
+ ActiveRecord::Base.connection.create_table(:comments, :force => true)
4
+ ActiveRecord::Base.connection.create_table(:posts, :force => true)
5
+ ActiveRecord::Base.connection.create_table(:users, :force => true) do |t|
6
+ t.string :firstname, :null => false
7
+ t.string :lastname, :null => false
8
+ t.boolean :enabled, :null => false
9
+ t.timestamps
10
+ end
11
+ ActiveRecord::Base.connection.create_table(:votes, :force => true)
@@ -0,0 +1,13 @@
1
+ module UserMacro
2
+
3
+ USERS = {}
4
+
5
+ def create_user(label, attributes)
6
+ USERS[label] = User.create!(attributes)
7
+ end
8
+
9
+ def users(label)
10
+ USERS[label]
11
+ end
12
+
13
+ end
metadata ADDED
@@ -0,0 +1,178 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: scoped_from
3
+ version: !ruby/object:Gem::Version
4
+ hash: 9
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 1
9
+ version: "0.1"
10
+ platform: ruby
11
+ authors:
12
+ - Alexis Toulotte
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-03-18 00:00:00 +11:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: activerecord
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ~>
27
+ - !ruby/object:Gem::Version
28
+ hash: 7
29
+ segments:
30
+ - 3
31
+ - 0
32
+ - 0
33
+ version: 3.0.0
34
+ type: :runtime
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: rspec
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
+ - 5
48
+ - 0
49
+ version: 2.5.0
50
+ type: :development
51
+ version_requirements: *id002
52
+ - !ruby/object:Gem::Dependency
53
+ name: rspec-rails
54
+ prerelease: false
55
+ requirement: &id003 !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ~>
59
+ - !ruby/object:Gem::Version
60
+ hash: 27
61
+ segments:
62
+ - 2
63
+ - 5
64
+ - 0
65
+ version: 2.5.0
66
+ type: :development
67
+ version_requirements: *id003
68
+ - !ruby/object:Gem::Dependency
69
+ name: ruby-debug
70
+ prerelease: false
71
+ requirement: &id004 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ hash: 3
77
+ segments:
78
+ - 0
79
+ version: "0"
80
+ type: :development
81
+ version_requirements: *id004
82
+ - !ruby/object:Gem::Dependency
83
+ name: sqlite3-ruby
84
+ prerelease: false
85
+ requirement: &id005 !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
+ type: :development
95
+ version_requirements: *id005
96
+ description: Provides a simple mapping between Active Record scopes and controller parameters for Ruby On Rails 3
97
+ email: al@alweb.org
98
+ executables: []
99
+
100
+ extensions: []
101
+
102
+ extra_rdoc_files: []
103
+
104
+ files:
105
+ - .gitignore
106
+ - .rspec
107
+ - Gemfile
108
+ - Gemfile.lock
109
+ - MIT-LICENSE
110
+ - README.mdown
111
+ - Rakefile
112
+ - VERSION
113
+ - init.rb
114
+ - lib/scoped_from.rb
115
+ - lib/scoped_from/active_record.rb
116
+ - lib/scoped_from/query.rb
117
+ - scoped_from.gemspec
118
+ - spec/mocks/comment.rb
119
+ - spec/mocks/comment_query.rb
120
+ - spec/mocks/post.rb
121
+ - spec/mocks/user.rb
122
+ - spec/mocks/user_query.rb
123
+ - spec/mocks/vote.rb
124
+ - spec/mocks/vote_query.rb
125
+ - spec/scoped_from/active_record_spec.rb
126
+ - spec/scoped_from/query_spec.rb
127
+ - spec/scoped_from_spec.rb
128
+ - spec/spec_helper.rb
129
+ - spec/support/bootsrap/database.rb
130
+ - spec/support/macros/user_macro.rb
131
+ has_rdoc: true
132
+ homepage: https://github.com/alexistoulotte/scoped_from
133
+ licenses: []
134
+
135
+ post_install_message:
136
+ rdoc_options: []
137
+
138
+ require_paths:
139
+ - lib
140
+ required_ruby_version: !ruby/object:Gem::Requirement
141
+ none: false
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ hash: 3
146
+ segments:
147
+ - 0
148
+ version: "0"
149
+ required_rubygems_version: !ruby/object:Gem::Requirement
150
+ none: false
151
+ requirements:
152
+ - - ">="
153
+ - !ruby/object:Gem::Version
154
+ hash: 3
155
+ segments:
156
+ - 0
157
+ version: "0"
158
+ requirements: []
159
+
160
+ rubyforge_project: scoped_from
161
+ rubygems_version: 1.5.1
162
+ signing_key:
163
+ specification_version: 3
164
+ summary: Mapping between scopes and parameters for Rails
165
+ test_files:
166
+ - spec/mocks/comment.rb
167
+ - spec/mocks/comment_query.rb
168
+ - spec/mocks/post.rb
169
+ - spec/mocks/user.rb
170
+ - spec/mocks/user_query.rb
171
+ - spec/mocks/vote.rb
172
+ - spec/mocks/vote_query.rb
173
+ - spec/scoped_from/active_record_spec.rb
174
+ - spec/scoped_from/query_spec.rb
175
+ - spec/scoped_from_spec.rb
176
+ - spec/spec_helper.rb
177
+ - spec/support/bootsrap/database.rb
178
+ - spec/support/macros/user_macro.rb