scoped_from 0.1

Sign up to get free protection for your applications and to get access to all the features.
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