paraphrase 0.7.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +12 -0
- data/Appraisals +4 -0
- data/CHANGELOG.md +53 -0
- data/README.md +132 -81
- data/gemfiles/3.0.gemfile +1 -0
- data/gemfiles/3.0.gemfile.lock +28 -2
- data/gemfiles/3.1.gemfile +1 -0
- data/gemfiles/3.1.gemfile.lock +28 -2
- data/gemfiles/3.2.gemfile +1 -0
- data/gemfiles/3.2.gemfile.lock +28 -2
- data/gemfiles/4.0.gemfile +1 -0
- data/gemfiles/4.0.gemfile.lock +14 -2
- data/lib/paraphrase/active_model.rb +30 -0
- data/lib/paraphrase/query.rb +66 -49
- data/lib/paraphrase/rails.rb +1 -2
- data/lib/paraphrase/scope.rb +56 -0
- data/lib/paraphrase/syntax.rb +15 -60
- data/lib/paraphrase/version.rb +1 -1
- data/lib/paraphrase.rb +1 -0
- data/paraphrase.gemspec +17 -4
- data/spec/paraphrase/query_spec.rb +113 -45
- data/spec/paraphrase/scope_spec.rb +51 -0
- data/spec/paraphrase/syntax_spec.rb +26 -7
- data/spec/spec_helper.rb +60 -1
- metadata +97 -57
- data/CHANGELOG +0 -39
- data/lib/paraphrase/scope_mapping.rb +0 -78
- data/spec/paraphrase/scope_mapping_spec.rb +0 -46
- data/spec/support/database.rb +0 -38
data/gemfiles/4.0.gemfile.lock
CHANGED
@@ -1,13 +1,20 @@
|
|
1
1
|
PATH
|
2
2
|
remote: /Users/edd_d/Work/paraphrase
|
3
3
|
specs:
|
4
|
-
paraphrase (0.
|
4
|
+
paraphrase (0.8.0)
|
5
|
+
activemodel (>= 3.0, < 4.1)
|
5
6
|
activerecord (>= 3.0, < 4.1)
|
6
7
|
activesupport (>= 3.0, < 4.1)
|
7
8
|
|
8
9
|
GEM
|
9
10
|
remote: https://rubygems.org/
|
10
11
|
specs:
|
12
|
+
actionpack (4.0.2)
|
13
|
+
activesupport (= 4.0.2)
|
14
|
+
builder (~> 3.1.0)
|
15
|
+
erubis (~> 2.7.0)
|
16
|
+
rack (~> 1.5.2)
|
17
|
+
rack-test (~> 0.6.2)
|
11
18
|
activemodel (4.0.2)
|
12
19
|
activesupport (= 4.0.2)
|
13
20
|
builder (~> 3.1.0)
|
@@ -31,6 +38,7 @@ GEM
|
|
31
38
|
builder (3.1.4)
|
32
39
|
coderay (1.1.0)
|
33
40
|
diff-lcs (1.2.5)
|
41
|
+
erubis (2.7.0)
|
34
42
|
i18n (0.6.9)
|
35
43
|
method_source (0.8.2)
|
36
44
|
minitest (4.7.5)
|
@@ -39,6 +47,9 @@ GEM
|
|
39
47
|
coderay (~> 1.0)
|
40
48
|
method_source (~> 0.8)
|
41
49
|
slop (~> 3.4)
|
50
|
+
rack (1.5.2)
|
51
|
+
rack-test (0.6.2)
|
52
|
+
rack (>= 1.0)
|
42
53
|
rake (0.9.6)
|
43
54
|
redcarpet (2.1.1)
|
44
55
|
rspec (2.14.1)
|
@@ -60,6 +71,7 @@ PLATFORMS
|
|
60
71
|
ruby
|
61
72
|
|
62
73
|
DEPENDENCIES
|
74
|
+
actionpack (~> 4.0)
|
63
75
|
activerecord (~> 4.0)
|
64
76
|
activesupport (~> 4.0)
|
65
77
|
appraisal (~> 0.4)
|
@@ -68,6 +80,6 @@ DEPENDENCIES
|
|
68
80
|
pry (~> 0.9)
|
69
81
|
rake (~> 0.9.2)
|
70
82
|
redcarpet (~> 2.1.1)
|
71
|
-
rspec (~> 2.
|
83
|
+
rspec (~> 2.14)
|
72
84
|
sqlite3 (~> 1.3.6)
|
73
85
|
yard (~> 0.7)
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
require 'active_model/naming'
|
3
|
+
|
4
|
+
module Paraphrase
|
5
|
+
module ActiveModel
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
def to_key
|
9
|
+
[:q]
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_model
|
13
|
+
self
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_param
|
17
|
+
params.to_param
|
18
|
+
end
|
19
|
+
|
20
|
+
def persisted?
|
21
|
+
true
|
22
|
+
end
|
23
|
+
|
24
|
+
module ClassMethods
|
25
|
+
def model_name
|
26
|
+
::ActiveModel::Name.new(self, nil, 'Q')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/paraphrase/query.rb
CHANGED
@@ -2,88 +2,105 @@ require 'active_support/core_ext/object/blank'
|
|
2
2
|
require 'active_support/core_ext/class/attribute'
|
3
3
|
require 'active_support/core_ext/module/delegation'
|
4
4
|
require 'active_support/core_ext/string/inflections'
|
5
|
+
require 'active_support/core_ext/array/extract_options'
|
5
6
|
require 'active_support/hash_with_indifferent_access'
|
7
|
+
require 'paraphrase/active_model'
|
6
8
|
|
7
9
|
module Paraphrase
|
8
10
|
class Query
|
9
|
-
|
10
|
-
#
|
11
|
-
|
12
|
-
|
13
|
-
# Delegate enumerable methods to results
|
14
|
-
delegate :collect, :map, :each, :select, :to_a, :to_ary, :to => :results
|
11
|
+
include ActiveModel
|
12
|
+
# @!attribute [r] scopes
|
13
|
+
# @return [Array<Scope>] scopes for query
|
14
|
+
class_attribute :scopes, :_source, instance_writer: false
|
15
15
|
|
16
16
|
# @!attribute [r] params
|
17
|
-
# @return [HashWithIndifferentAccess] filters parameters based on keys defined in
|
17
|
+
# @return [HashWithIndifferentAccess] filters parameters based on keys defined in scopes
|
18
18
|
#
|
19
|
-
# @!attribute [r]
|
19
|
+
# @!attribute [r] result
|
20
20
|
# @return [ActiveRecord::Relation]
|
21
|
-
attr_reader :params, :
|
21
|
+
attr_reader :params, :result
|
22
22
|
|
23
|
-
# Set `
|
23
|
+
# Set `scopes` on inheritance to ensure they're unique per subclass
|
24
24
|
def self.inherited(klass)
|
25
|
-
klass.
|
25
|
+
klass.scopes = []
|
26
|
+
end
|
27
|
+
|
28
|
+
# Specify the `ActiveRecord` source class if not determinable from the name
|
29
|
+
# of the `Paraphrase::Query` subclass.
|
30
|
+
#
|
31
|
+
# @param [String, Symbol] name name of the source class
|
32
|
+
def self.source(name)
|
33
|
+
self._source = name.to_s
|
26
34
|
end
|
27
35
|
|
28
|
-
# Add a {
|
36
|
+
# Add a {Scope} instance to {Query#scopes}. Defines a reader for each key
|
37
|
+
# to read from {Query#params}.
|
29
38
|
#
|
30
|
-
# @see
|
31
|
-
def self.map(
|
32
|
-
|
33
|
-
|
39
|
+
# @see Scope#initialize
|
40
|
+
def self.map(*keys)
|
41
|
+
options = keys.extract_options!
|
42
|
+
scope_name = options[:to]
|
43
|
+
|
44
|
+
if scopes.any? { |scope| scope.name == scope_name }
|
45
|
+
raise DuplicateScopeError, "scope :#{scope_name} has already been mapped"
|
34
46
|
end
|
35
47
|
|
36
|
-
|
48
|
+
scopes << Scope.new(keys, options)
|
49
|
+
|
50
|
+
keys.each do |key|
|
51
|
+
define_method(key) { params[key] } unless method_defined?(key)
|
52
|
+
end
|
37
53
|
end
|
38
54
|
|
39
55
|
# Filters out parameters irrelevant to the query and sets the base scope
|
40
56
|
# for to begin the chain.
|
41
57
|
#
|
42
58
|
# @param [Hash] params query parameters
|
43
|
-
# @param [ActiveRecord::
|
44
|
-
|
45
|
-
|
46
|
-
keys = mappings.map(&:keys).flatten.map(&:to_s)
|
59
|
+
# @param [ActiveRecord::Relation] relation object to apply methods to
|
60
|
+
def initialize(params = {}, relation = source)
|
61
|
+
keys = scopes.map(&:keys).flatten.map(&:to_s)
|
47
62
|
|
48
|
-
@params =
|
49
|
-
|
50
|
-
@params.freeze
|
63
|
+
@params = params.with_indifferent_access.slice(*keys)
|
64
|
+
scrub_params!
|
51
65
|
|
52
|
-
@
|
66
|
+
@result = scopes.inject(relation) do |r, scope|
|
67
|
+
scope.chain(self, r)
|
68
|
+
end
|
53
69
|
end
|
54
70
|
|
55
|
-
|
56
|
-
# are missing for a required key, an empty array is returned.
|
57
|
-
#
|
58
|
-
# @return [ActiveRecord::Relation, Array]
|
59
|
-
def results
|
60
|
-
return @results if @results
|
61
|
-
|
62
|
-
ActiveSupport::Notifications.instrument('query.paraphrase', :params => params, :source_name => source.name, :source => source) do
|
63
|
-
@results = mappings.inject(source) do |query, scope|
|
64
|
-
query = scope.chain(params, query)
|
71
|
+
alias :[] :send
|
65
72
|
|
66
|
-
|
67
|
-
|
68
|
-
|
73
|
+
# Return an `ActiveRecord::Relation` corresponding to the source class
|
74
|
+
# determined from the `_source` class attribute or the name of the query
|
75
|
+
# class.
|
76
|
+
#
|
77
|
+
# @return [ActiveRecord::Relation]
|
78
|
+
def source
|
79
|
+
@source ||= begin
|
80
|
+
name = _source || self.class.to_s.sub(/Query$/, '')
|
81
|
+
name.constantize
|
69
82
|
end
|
70
83
|
end
|
71
84
|
|
72
|
-
|
73
|
-
super || results.respond_to?(name, include_private)
|
74
|
-
end
|
85
|
+
private
|
75
86
|
|
76
|
-
|
87
|
+
def scrub_params!
|
88
|
+
params.delete_if { |key, value| scrub(value) }
|
89
|
+
end
|
77
90
|
|
78
|
-
def
|
79
|
-
|
80
|
-
|
81
|
-
|
91
|
+
def scrub(value)
|
92
|
+
value = case value
|
93
|
+
when Array
|
94
|
+
value.delete_if { |v| scrub(v) }
|
95
|
+
when Hash
|
96
|
+
value.delete_if { |k, v| scrub(v) }
|
97
|
+
when String
|
98
|
+
value.strip
|
82
99
|
else
|
83
|
-
|
100
|
+
value
|
84
101
|
end
|
102
|
+
|
103
|
+
value.blank?
|
85
104
|
end
|
86
105
|
end
|
87
106
|
end
|
88
|
-
|
89
|
-
require 'paraphrase/scope_mapping'
|
data/lib/paraphrase/rails.rb
CHANGED
@@ -4,8 +4,7 @@ module Paraphrase
|
|
4
4
|
class Railtie < Rails::Railtie
|
5
5
|
initializer 'paraphrase.extend_active_record' do
|
6
6
|
ActiveSupport.on_load :active_record do
|
7
|
-
extend Paraphrase::Syntax
|
8
|
-
ActiveRecord::Relation.send(:include, Paraphrase::Syntax::Relation)
|
7
|
+
extend Paraphrase::Syntax
|
9
8
|
end
|
10
9
|
end
|
11
10
|
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'active_support/core_ext/object/blank'
|
2
|
+
require 'active_support/core_ext/array/wrap'
|
3
|
+
|
4
|
+
module Paraphrase
|
5
|
+
class Scope
|
6
|
+
# @!attribute [r] keys
|
7
|
+
# @return [Array<Symbol>] param keys to extract
|
8
|
+
#
|
9
|
+
# @!attribute [r] name
|
10
|
+
# @return [Symbol] scope name
|
11
|
+
#
|
12
|
+
# @!attribute [r] required_keys
|
13
|
+
# @return [Array<Symbol>] keys required for query
|
14
|
+
attr_reader :keys, :name, :required_keys
|
15
|
+
|
16
|
+
# @param [Symbol] name name of the scope
|
17
|
+
# @param [Hash] options options to configure {Scope Scope} instance
|
18
|
+
# @option options [Symbol, Array<Symbol>] :to param key(s) to extract values from
|
19
|
+
# @option options [true, Symbol, Array<Symbol>] :require lists all or a
|
20
|
+
# subset of param keys as required
|
21
|
+
def initialize(keys, options)
|
22
|
+
@keys = keys
|
23
|
+
@name = options[:to]
|
24
|
+
|
25
|
+
@required_keys = if options[:whitelist] == true
|
26
|
+
[]
|
27
|
+
else
|
28
|
+
@keys - Array.wrap(options[:whitelist])
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Sends {#name} to `relation` if `query` has a value for all the
|
33
|
+
# {Scope#required_keys}. Passes through `relation` if any
|
34
|
+
# values are missing. Detects if the scope takes no arguments to determine
|
35
|
+
# if values should be passed to the scope.
|
36
|
+
#
|
37
|
+
# @param [Paraphrase::Query] query instance of {Query} class
|
38
|
+
# @param [ActiveRecord::Relation] relation scope chain
|
39
|
+
# @return [ActiveRecord::Relation]
|
40
|
+
def chain(query, relation)
|
41
|
+
if required_keys.all? { |key| query[key] }
|
42
|
+
klass = relation.respond_to?(:klass) ? relation.klass : relation
|
43
|
+
arity = klass.method(name).arity
|
44
|
+
|
45
|
+
if arity == 0
|
46
|
+
relation.send(name)
|
47
|
+
else
|
48
|
+
values = keys.map { |key| query[key] }
|
49
|
+
relation.send(name, *values)
|
50
|
+
end
|
51
|
+
else
|
52
|
+
relation
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/lib/paraphrase/syntax.rb
CHANGED
@@ -1,68 +1,23 @@
|
|
1
1
|
module Paraphrase
|
2
2
|
module Syntax
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
# scopes to param keys
|
12
|
-
#
|
13
|
-
# @param [Proc] &block block to to define scope mappings
|
14
|
-
def register_mapping(&block)
|
15
|
-
self._paraphraser = Class.new(Query, &block)
|
16
|
-
end
|
17
|
-
|
18
|
-
# Attempts to find paraphrase class based on class name. Override if
|
19
|
-
# using a different naming convention.
|
20
|
-
def paraphraser
|
21
|
-
self._paraphraser || "#{self.name}Query".constantize
|
22
|
-
rescue
|
23
|
-
nil
|
24
|
-
end
|
25
|
-
|
26
|
-
# Instantiate the {Query} class that is mapped to `self`.
|
27
|
-
#
|
28
|
-
# @param [Hash] params query parameters
|
29
|
-
def paraphrase(params)
|
30
|
-
self.paraphraser.new(params, self)
|
3
|
+
# Attempts to find paraphrase class based on class name. Override if
|
4
|
+
# using a different naming convention.
|
5
|
+
def paraphraser
|
6
|
+
name = "#{self.name}Query"
|
7
|
+
name.constantize
|
8
|
+
rescue NameError => e
|
9
|
+
if e.message =~ /uninitialized constant/
|
10
|
+
raise Paraphrase::NoQueryDefined.new("No query class found. #{name} must be defined as a subclass of Paraphrase::Query")
|
31
11
|
end
|
32
12
|
end
|
33
13
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
#
|
40
|
-
# ```ruby
|
41
|
-
# class User < ActiveRecord::Base
|
42
|
-
# has_many :posts
|
43
|
-
# end
|
44
|
-
#
|
45
|
-
# class Post < ActiveRecord::Base
|
46
|
-
# belongs_to :user
|
47
|
-
#
|
48
|
-
# register_mapping
|
49
|
-
# map :title_like, :to => :title
|
50
|
-
# end
|
51
|
-
# end
|
52
|
-
# ```
|
53
|
-
#
|
54
|
-
# It is possible to do the following:
|
55
|
-
#
|
56
|
-
# ```ruby
|
57
|
-
# user.posts.paraphrase({ :title => 'Game of Thrones Finale' }).to_sql
|
58
|
-
# # => SELECT `posts`.* FROM `posts` INNER JOIN `users` ON `users`.`post_id` = `posts`.`id` WHERE `posts`.`title LIKE "%Game of Thrones Finale%";
|
59
|
-
# ```
|
60
|
-
#
|
61
|
-
# @param [Hash] params query parameters
|
62
|
-
# @return [Paraphrase::Query]
|
63
|
-
def paraphrase(params)
|
64
|
-
klass.paraphraser.new(params, self)
|
65
|
-
end
|
14
|
+
# Instantiate the {Query} class that is mapped to `self`.
|
15
|
+
#
|
16
|
+
# @param [Hash] params query parameters
|
17
|
+
def paraphrase(params = {})
|
18
|
+
paraphraser.new(params, self).result
|
66
19
|
end
|
67
20
|
end
|
21
|
+
|
22
|
+
class NoQueryDefined < StandardError; end
|
68
23
|
end
|
data/lib/paraphrase/version.rb
CHANGED
data/lib/paraphrase.rb
CHANGED
data/paraphrase.gemspec
CHANGED
@@ -14,7 +14,7 @@ Gem::Specification.new do |gem|
|
|
14
14
|
}
|
15
15
|
gem.license = "MIT"
|
16
16
|
gem.authors = ["Eduardo Gutierrez"]
|
17
|
-
gem.email = "
|
17
|
+
gem.email = "eduardo@vermonster.com"
|
18
18
|
gem.homepage = "https://github.com/ecbypi/paraphrase"
|
19
19
|
|
20
20
|
gem.files = `git ls-files`.split($/)
|
@@ -22,15 +22,28 @@ Gem::Specification.new do |gem|
|
|
22
22
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
23
23
|
gem.require_paths = ['lib']
|
24
24
|
|
25
|
+
gem.required_ruby_version = '>= 1.9.3'
|
26
|
+
|
25
27
|
gem.add_dependency 'activerecord', '>= 3.0', '< 4.1'
|
26
28
|
gem.add_dependency 'activesupport', '>= 3.0', '< 4.1'
|
29
|
+
gem.add_dependency 'activemodel', '>= 3.0', '< 4.1'
|
27
30
|
|
31
|
+
gem.add_development_dependency 'actionpack', '>= 3.0', '< 4.1'
|
28
32
|
gem.add_development_dependency 'bundler', '~> 1.0'
|
29
33
|
gem.add_development_dependency 'yard', '~> 0.7'
|
30
|
-
gem.add_development_dependency 'rspec', '~> 2.
|
31
|
-
gem.add_development_dependency 'redcarpet', '~> 2.1.1'
|
34
|
+
gem.add_development_dependency 'rspec', '~> 2.14'
|
32
35
|
gem.add_development_dependency 'rake', '~> 0.9.2'
|
33
|
-
gem.add_development_dependency 'sqlite3', '~> 1.3.6'
|
34
36
|
gem.add_development_dependency 'appraisal', '~> 0.4'
|
35
37
|
gem.add_development_dependency 'pry', '~> 0.9'
|
38
|
+
|
39
|
+
if RUBY_PLATFORM != 'java'
|
40
|
+
gem.add_development_dependency 'redcarpet', '~> 2.1.1'
|
41
|
+
end
|
42
|
+
|
43
|
+
if RUBY_PLATFORM == 'java'
|
44
|
+
gem.add_development_dependency 'activerecord-jdbcsqlite3-adapter'
|
45
|
+
gem.add_development_dependency 'jdbc-sqlite3'
|
46
|
+
else
|
47
|
+
gem.add_development_dependency 'sqlite3', '~> 1.3.6'
|
48
|
+
end
|
36
49
|
end
|
@@ -1,83 +1,151 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
+
require 'action_view/test_case'
|
2
3
|
|
3
4
|
module Paraphrase
|
4
5
|
describe Query do
|
5
|
-
|
6
|
-
|
6
|
+
class ::PostQuery < Paraphrase::Query
|
7
|
+
map :title, to: :titled
|
8
|
+
map :is_published, to: :published
|
9
|
+
map :authors, to: :by_users
|
10
|
+
map :start_date, :end_date, to: :published_between
|
11
|
+
|
12
|
+
def start_date
|
13
|
+
@start_date ||= Time.parse(params[:start_date]) rescue nil
|
14
|
+
end
|
7
15
|
|
8
|
-
|
9
|
-
|
16
|
+
def end_date
|
17
|
+
@end_date ||= Time.parse(params[:end_date]) rescue nil
|
18
|
+
end
|
19
|
+
end
|
10
20
|
|
11
|
-
|
21
|
+
describe ".map" do
|
22
|
+
it "adds information to Query.scopes" do
|
23
|
+
expect(PostQuery.scopes).not_to be_empty
|
12
24
|
end
|
13
25
|
|
14
26
|
it "raises an error if a scope is added twice" do
|
15
|
-
|
27
|
+
expect { PostQuery.map :name, to: :titled }.to raise_error Paraphrase::DuplicateScopeError
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'defines readers for each key' do
|
31
|
+
query = PostQuery.new
|
16
32
|
|
17
|
-
expect
|
33
|
+
expect(query).to respond_to :title
|
34
|
+
expect(query).to respond_to :is_published
|
35
|
+
expect(query).to respond_to :authors
|
18
36
|
end
|
19
37
|
end
|
20
38
|
|
21
|
-
|
22
|
-
|
39
|
+
after do
|
40
|
+
Post.delete_all
|
41
|
+
User.delete_all
|
42
|
+
end
|
43
|
+
|
44
|
+
describe '#source' do
|
45
|
+
it 'is determined via query class name' do
|
46
|
+
expect(PostQuery.new.result).to eq Post
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'can be manually specified in the class' do
|
23
50
|
klass = Class.new(Query) do
|
24
|
-
|
25
|
-
map :email_like, :to => :email
|
51
|
+
source :User
|
26
52
|
end
|
27
53
|
|
28
|
-
klass.new
|
54
|
+
expect(klass.new.result).to eq User
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe '#[]' do
|
59
|
+
it 'retreives values from #params or uses custom reader if defined' do
|
60
|
+
query = PostQuery.new(title: 'Morning Joe', start_date: '2010-10-30', end_date: 'foo')
|
61
|
+
|
62
|
+
expect(query[:title]).to eq 'Morning Joe'
|
63
|
+
expect(query[:start_date]).to eq Time.local(2010, 10, 30)
|
64
|
+
expect(query[:end_date]).to be_nil
|
29
65
|
end
|
66
|
+
end
|
30
67
|
|
31
|
-
|
32
|
-
|
33
|
-
query.
|
68
|
+
describe "#params" do
|
69
|
+
it "filters out params not specified in scopes" do
|
70
|
+
query = PostQuery.new(nickname: 'bill', title: 'william')
|
71
|
+
|
72
|
+
expect(query.params).not_to have_key :nickname
|
73
|
+
expect(query.params).to have_key :title
|
34
74
|
end
|
35
75
|
|
36
76
|
it "sets up params with indifferent access" do
|
37
|
-
query.
|
77
|
+
query = PostQuery.new(title: 'D3 How-To')
|
78
|
+
expect(query.params).to have_key 'title'
|
38
79
|
end
|
39
80
|
|
40
|
-
it 'filters out blank values' do
|
41
|
-
query.
|
81
|
+
it 'recursively filters out blank values' do
|
82
|
+
query = PostQuery.new(title: { key: ['', { key: [] }, []] }, authors: ['', 'kevin', ['', {}], { key: [' '] }])
|
83
|
+
|
84
|
+
expect(query.params[:authors]).to eq ['kevin']
|
85
|
+
expect(query.params).not_to have_key :title
|
42
86
|
end
|
43
87
|
end
|
44
88
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
89
|
+
it 'skips scopes if query params are missing' do
|
90
|
+
expect(Post).not_to receive(:published_between)
|
91
|
+
expect(Post).not_to receive(:titled)
|
92
|
+
expect(Post).not_to receive(:by_users)
|
93
|
+
expect(Post).to receive(:published)
|
94
|
+
|
95
|
+
PostQuery.new(
|
96
|
+
start_date: Time.local(2010, 10, 30),
|
97
|
+
end_date: 'foo',
|
98
|
+
is_published: '1',
|
99
|
+
authors: [],
|
100
|
+
title: ['', {}]
|
101
|
+
)
|
102
|
+
end
|
52
103
|
|
53
|
-
|
54
|
-
|
55
|
-
|
104
|
+
it 'preserves the original scope used to initialize the query' do
|
105
|
+
user = User.create!
|
106
|
+
blue_post = Post.create!(user: user, title: 'Blue', published: false)
|
107
|
+
red_post = Post.create!(user: user, title: 'Red', published: true)
|
108
|
+
green_post = Post.create!(title: 'Red', published: true)
|
56
109
|
|
57
|
-
|
58
|
-
query.results
|
59
|
-
end
|
110
|
+
result = PostQuery.new({ title: 'Red' }, user.posts.published).result
|
60
111
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
end
|
112
|
+
expect(result).to include red_post
|
113
|
+
expect(result).not_to include blue_post
|
114
|
+
expect(result).not_to include green_post
|
65
115
|
end
|
66
116
|
|
67
|
-
describe
|
68
|
-
it
|
69
|
-
|
70
|
-
|
117
|
+
describe 'is action view compliant' do
|
118
|
+
it 'by working with form builders' do
|
119
|
+
router = ActionDispatch::Routing::RouteSet.new
|
120
|
+
router.draw do
|
121
|
+
resources :posts
|
122
|
+
end
|
123
|
+
|
124
|
+
controller = ActionView::TestCase::TestController.new
|
125
|
+
controller.instance_variable_set(:@_routes, router)
|
126
|
+
controller.class_eval { include router.url_helpers }
|
127
|
+
controller.view_context.class_eval { include router.url_helpers }
|
128
|
+
|
129
|
+
query = PostQuery.new(title: 'Red', start_date: '2012-10-01')
|
130
|
+
|
131
|
+
markup = ""
|
132
|
+
controller.view_context.form_for query, url: router.url_helpers.posts_path do |f|
|
133
|
+
markup << f.text_field(:title)
|
134
|
+
markup << f.date_select(:start_date)
|
71
135
|
end
|
72
136
|
|
73
|
-
|
74
|
-
|
75
|
-
|
137
|
+
expect(markup).to match(/<input.*type="text"/)
|
138
|
+
expect(markup).to match(/type="text"/)
|
139
|
+
expect(markup).to match(/name="q\[title\]"/)
|
140
|
+
expect(markup).to match(/value="Red"/)
|
76
141
|
|
77
|
-
|
78
|
-
|
142
|
+
expect(markup).to match(/<select.*name="q\[start_date\(1i\)/)
|
143
|
+
expect(markup).to match(/<select.*name="q\[start_date\(2i\)/)
|
144
|
+
expect(markup).to match(/<select.*name="q\[start_date\(3i\)/)
|
79
145
|
|
80
|
-
|
146
|
+
expect(markup).to match(/<option.*selected="selected" value="2012"/)
|
147
|
+
expect(markup).to match(/<option.*selected="selected" value="10"/)
|
148
|
+
expect(markup).to match(/<option.*selected="selected" value="1"/)
|
81
149
|
end
|
82
150
|
end
|
83
151
|
end
|