paraphrase 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|