paraphrase 0.9.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
data/gemfiles/4.0.gemfile CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "activerecord", "~> 4.0"
6
- gem "activesupport", "~> 4.0"
7
- gem "actionpack", "~> 4.0"
5
+ gem "activerecord", "~> 4.0.0"
6
+ gem "activesupport", "~> 4.0.0"
7
+ gem "actionpack", "~> 4.0.0"
8
8
 
9
9
  gemspec :path=>"../"
@@ -1,31 +1,31 @@
1
1
  PATH
2
- remote: /Users/edd_d/Work/paraphrase
2
+ remote: ../
3
3
  specs:
4
- paraphrase (0.9.0)
5
- activemodel (>= 3.0, < 4.2)
6
- activerecord (>= 3.0, < 4.2)
7
- activesupport (>= 3.0, < 4.2)
4
+ paraphrase (0.10.0)
5
+ activemodel (>= 3.1, < 4.2)
6
+ activerecord (>= 3.1, < 4.2)
7
+ activesupport (>= 3.1, < 4.2)
8
8
 
9
9
  GEM
10
10
  remote: https://rubygems.org/
11
11
  specs:
12
- actionpack (4.0.2)
13
- activesupport (= 4.0.2)
12
+ actionpack (4.0.4)
13
+ activesupport (= 4.0.4)
14
14
  builder (~> 3.1.0)
15
15
  erubis (~> 2.7.0)
16
16
  rack (~> 1.5.2)
17
17
  rack-test (~> 0.6.2)
18
- activemodel (4.0.2)
19
- activesupport (= 4.0.2)
18
+ activemodel (4.0.4)
19
+ activesupport (= 4.0.4)
20
20
  builder (~> 3.1.0)
21
- activerecord (4.0.2)
22
- activemodel (= 4.0.2)
21
+ activerecord (4.0.4)
22
+ activemodel (= 4.0.4)
23
23
  activerecord-deprecated_finders (~> 1.0.2)
24
- activesupport (= 4.0.2)
24
+ activesupport (= 4.0.4)
25
25
  arel (~> 4.0.0)
26
26
  activerecord-deprecated_finders (1.0.3)
27
- activesupport (4.0.2)
28
- i18n (~> 0.6, >= 0.6.4)
27
+ activesupport (4.0.4)
28
+ i18n (~> 0.6, >= 0.6.9)
29
29
  minitest (~> 4.2)
30
30
  multi_json (~> 1.3)
31
31
  thread_safe (~> 0.1)
@@ -33,8 +33,7 @@ GEM
33
33
  appraisal (0.5.2)
34
34
  bundler
35
35
  rake
36
- arel (4.0.1)
37
- atomic (1.1.14)
36
+ arel (4.0.2)
38
37
  builder (3.1.4)
39
38
  codeclimate-test-reporter (0.3.0)
40
39
  simplecov (>= 0.7.1, < 1.0.0)
@@ -45,8 +44,8 @@ GEM
45
44
  i18n (0.6.9)
46
45
  method_source (0.8.2)
47
46
  minitest (4.7.5)
48
- multi_json (1.8.4)
49
- pry (0.9.12.4)
47
+ multi_json (1.10.1)
48
+ pry (0.9.12.6)
50
49
  coderay (~> 1.0)
51
50
  method_source (~> 0.8)
52
51
  slop (~> 3.4)
@@ -55,33 +54,36 @@ GEM
55
54
  rack (>= 1.0)
56
55
  rake (0.9.6)
57
56
  redcarpet (2.1.1)
58
- rspec (2.14.1)
59
- rspec-core (~> 2.14.0)
60
- rspec-expectations (~> 2.14.0)
61
- rspec-mocks (~> 2.14.0)
62
- rspec-core (2.14.7)
63
- rspec-expectations (2.14.4)
64
- diff-lcs (>= 1.1.3, < 2.0)
65
- rspec-mocks (2.14.4)
57
+ rspec (3.0.0)
58
+ rspec-core (~> 3.0.0)
59
+ rspec-expectations (~> 3.0.0)
60
+ rspec-mocks (~> 3.0.0)
61
+ rspec-core (3.0.0)
62
+ rspec-support (~> 3.0.0)
63
+ rspec-expectations (3.0.0)
64
+ diff-lcs (>= 1.2.0, < 2.0)
65
+ rspec-support (~> 3.0.0)
66
+ rspec-mocks (3.0.0)
67
+ rspec-support (~> 3.0.0)
68
+ rspec-support (3.0.0)
66
69
  simplecov (0.8.2)
67
70
  docile (~> 1.1.0)
68
71
  multi_json
69
72
  simplecov-html (~> 0.8.0)
70
73
  simplecov-html (0.8.0)
71
- slop (3.4.7)
72
- sqlite3 (1.3.8)
73
- thread_safe (0.1.3)
74
- atomic
75
- tzinfo (0.3.38)
76
- yard (0.8.7.3)
74
+ slop (3.5.0)
75
+ sqlite3 (1.3.9)
76
+ thread_safe (0.3.3)
77
+ tzinfo (0.3.39)
78
+ yard (0.8.7.4)
77
79
 
78
80
  PLATFORMS
79
81
  ruby
80
82
 
81
83
  DEPENDENCIES
82
- actionpack (~> 4.0)
83
- activerecord (~> 4.0)
84
- activesupport (~> 4.0)
84
+ actionpack (~> 4.0.0)
85
+ activerecord (~> 4.0.0)
86
+ activesupport (~> 4.0.0)
85
87
  appraisal (~> 0.4)
86
88
  bundler (~> 1.0)
87
89
  codeclimate-test-reporter (~> 0.3)
@@ -89,6 +91,6 @@ DEPENDENCIES
89
91
  pry (~> 0.9)
90
92
  rake (~> 0.9.2)
91
93
  redcarpet (~> 2.1.1)
92
- rspec (~> 2.14)
94
+ rspec (~> 3.0)
93
95
  sqlite3 (~> 1.3.6)
94
96
  yard (~> 0.7)
data/gemfiles/4.1.gemfile CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "activerecord", "~> 4.1"
6
- gem "activesupport", "~> 4.1"
7
- gem "actionpack", "~> 4.1"
5
+ gem "activerecord", "~> 4.1.0"
6
+ gem "activesupport", "~> 4.1.0"
7
+ gem "actionpack", "~> 4.1.0"
8
8
 
9
9
  gemspec :path=>"../"
@@ -1,31 +1,31 @@
1
1
  PATH
2
2
  remote: ../
3
3
  specs:
4
- paraphrase (0.9.0)
5
- activemodel (>= 3.0, < 4.2)
6
- activerecord (>= 3.0, < 4.2)
7
- activesupport (>= 3.0, < 4.2)
4
+ paraphrase (0.10.0)
5
+ activemodel (>= 3.1, < 4.2)
6
+ activerecord (>= 3.1, < 4.2)
7
+ activesupport (>= 3.1, < 4.2)
8
8
 
9
9
  GEM
10
10
  remote: https://rubygems.org/
11
11
  specs:
12
- actionpack (4.1.0)
13
- actionview (= 4.1.0)
14
- activesupport (= 4.1.0)
12
+ actionpack (4.1.1)
13
+ actionview (= 4.1.1)
14
+ activesupport (= 4.1.1)
15
15
  rack (~> 1.5.2)
16
16
  rack-test (~> 0.6.2)
17
- actionview (4.1.0)
18
- activesupport (= 4.1.0)
17
+ actionview (4.1.1)
18
+ activesupport (= 4.1.1)
19
19
  builder (~> 3.1)
20
20
  erubis (~> 2.7.0)
21
- activemodel (4.1.0)
22
- activesupport (= 4.1.0)
21
+ activemodel (4.1.1)
22
+ activesupport (= 4.1.1)
23
23
  builder (~> 3.1)
24
- activerecord (4.1.0)
25
- activemodel (= 4.1.0)
26
- activesupport (= 4.1.0)
24
+ activerecord (4.1.1)
25
+ activemodel (= 4.1.1)
26
+ activesupport (= 4.1.1)
27
27
  arel (~> 5.0.0)
28
- activesupport (4.1.0)
28
+ activesupport (4.1.1)
29
29
  i18n (~> 0.6, >= 0.6.9)
30
30
  json (~> 1.7, >= 1.7.7)
31
31
  minitest (~> 5.1)
@@ -45,8 +45,8 @@ GEM
45
45
  i18n (0.6.9)
46
46
  json (1.8.1)
47
47
  method_source (0.8.2)
48
- minitest (5.3.3)
49
- multi_json (1.9.3)
48
+ minitest (5.3.4)
49
+ multi_json (1.10.1)
50
50
  pry (0.9.12.6)
51
51
  coderay (~> 1.0)
52
52
  method_source (~> 0.8)
@@ -56,33 +56,37 @@ GEM
56
56
  rack (>= 1.0)
57
57
  rake (0.9.6)
58
58
  redcarpet (2.1.1)
59
- rspec (2.14.1)
60
- rspec-core (~> 2.14.0)
61
- rspec-expectations (~> 2.14.0)
62
- rspec-mocks (~> 2.14.0)
63
- rspec-core (2.14.8)
64
- rspec-expectations (2.14.5)
65
- diff-lcs (>= 1.1.3, < 2.0)
66
- rspec-mocks (2.14.6)
59
+ rspec (3.0.0)
60
+ rspec-core (~> 3.0.0)
61
+ rspec-expectations (~> 3.0.0)
62
+ rspec-mocks (~> 3.0.0)
63
+ rspec-core (3.0.0)
64
+ rspec-support (~> 3.0.0)
65
+ rspec-expectations (3.0.0)
66
+ diff-lcs (>= 1.2.0, < 2.0)
67
+ rspec-support (~> 3.0.0)
68
+ rspec-mocks (3.0.0)
69
+ rspec-support (~> 3.0.0)
70
+ rspec-support (3.0.0)
67
71
  simplecov (0.8.2)
68
72
  docile (~> 1.1.0)
69
73
  multi_json
70
74
  simplecov-html (~> 0.8.0)
71
75
  simplecov-html (0.8.0)
72
76
  slop (3.5.0)
73
- sqlite3 (1.3.8)
77
+ sqlite3 (1.3.9)
74
78
  thread_safe (0.3.3)
75
79
  tzinfo (1.1.0)
76
80
  thread_safe (~> 0.1)
77
- yard (0.8.7.3)
81
+ yard (0.8.7.4)
78
82
 
79
83
  PLATFORMS
80
84
  ruby
81
85
 
82
86
  DEPENDENCIES
83
- actionpack (~> 4.1)
84
- activerecord (~> 4.1)
85
- activesupport (~> 4.1)
87
+ actionpack (~> 4.1.0)
88
+ activerecord (~> 4.1.0)
89
+ activesupport (~> 4.1.0)
86
90
  appraisal (~> 0.4)
87
91
  bundler (~> 1.0)
88
92
  codeclimate-test-reporter (~> 0.3)
@@ -90,6 +94,6 @@ DEPENDENCIES
90
94
  pry (~> 0.9)
91
95
  rake (~> 0.9.2)
92
96
  redcarpet (~> 2.1.1)
93
- rspec (~> 2.14)
97
+ rspec (~> 3.0)
94
98
  sqlite3 (~> 1.3.6)
95
99
  yard (~> 0.7)
data/lib/paraphrase.rb CHANGED
@@ -1,5 +1,16 @@
1
- require 'paraphrase/errors'
1
+ module Paraphrase
2
+ class DuplicateMappingError < StandardError
3
+ def initialize(scope_name)
4
+ @message = "scope :#{scope_name} has already been mapped"
5
+ end
6
+ end
7
+
8
+ class NoQueryDefined < StandardError
9
+ def initialize(query_name)
10
+ @message = "No query class found. #{query_name} must be defined as a subclass of `Paraphrase::Query`"
11
+ end
12
+ end
13
+ end
14
+
2
15
  require 'paraphrase/query'
3
- require 'paraphrase/scope'
4
- require 'paraphrase/syntax'
5
16
  require 'paraphrase/rails' if defined?(Rails)
@@ -2,6 +2,7 @@ require 'active_support/concern'
2
2
  require 'active_model/naming'
3
3
 
4
4
  module Paraphrase
5
+ # @api private
5
6
  module ActiveModel
6
7
  extend ActiveSupport::Concern
7
8
 
@@ -0,0 +1,28 @@
1
+ require 'active_support/core_ext/object/blank'
2
+ require 'active_support/core_ext/array/wrap'
3
+
4
+ module Paraphrase
5
+ # @api private
6
+ class Mapping
7
+ attr_reader :keys, :name, :required_keys
8
+
9
+ def initialize(keys, options)
10
+ @keys = keys
11
+ @name = options[:to]
12
+
13
+ @required_keys = if options[:whitelist] == true
14
+ []
15
+ else
16
+ @keys - Array.wrap(options[:whitelist])
17
+ end
18
+ end
19
+
20
+ def satisfied?(params)
21
+ required_keys.all? { |key| params[key] }
22
+ end
23
+
24
+ def values(params)
25
+ keys.map { |key| params[key] }
26
+ end
27
+ end
28
+ end
@@ -1,12 +1,18 @@
1
1
  module Paraphrase
2
- class Params
2
+ class ParamsFilter
3
3
  attr_reader :params, :result
4
4
 
5
- def initialize(params, keys)
6
- @params = params.with_indifferent_access.slice(*keys)
5
+ def initialize(unfiltered_params, keys)
6
+ @params = unfiltered_params.with_indifferent_access.slice(*keys)
7
7
 
8
8
  @result = @params.inject(HashWithIndifferentAccess.new) do |result, (key, value)|
9
- value = respond_to?(key) ? send(key) : scrub(@params[key])
9
+ value = @params[key]
10
+
11
+ if respond_to?(key)
12
+ value = send(key)
13
+ end
14
+
15
+ value = scrub(value)
10
16
 
11
17
  if value.present?
12
18
  result[key] = value
@@ -4,92 +4,115 @@ require 'active_support/core_ext/module/delegation'
4
4
  require 'active_support/core_ext/string/inflections'
5
5
  require 'active_support/core_ext/array/extract_options'
6
6
  require 'active_support/hash_with_indifferent_access'
7
+
7
8
  require 'paraphrase/active_model'
8
- require 'paraphrase/params'
9
+ require 'paraphrase/mapping'
10
+ require 'paraphrase/params_filter'
11
+ require 'paraphrase/repository'
9
12
 
10
13
  module Paraphrase
11
14
  class Query
12
15
  include ActiveModel
13
- # @!attribute [r] scopes
14
- # @return [Array<Scope>] scopes for query
15
- class_attribute :scopes, :_source, instance_writer: false
16
+ # @!attribute [r] mappings
17
+ # @return [Array<Paraphrase::Mapping>] mappings for query
18
+ # @!attribute [r] source
19
+ # @return [Symbol, String] name of the class to use as the source for the
20
+ # query
21
+ class_attribute :mappings, instance_writer: false
22
+ class_attribute :source, instance_writer: false, instance_reader: false
23
+ class_attribute :params_filter, instance_writer: false
24
+ class_attribute :repository, instance_writer: false
16
25
 
17
26
  # @!attribute [r] params
18
- # @return [HashWithIndifferentAccess] filtered parameters based on keys defined in scopes
27
+ # @return [HashWithIndifferentAccess] filtered parameters based on keys defined in `mappings`
19
28
  #
20
29
  # @!attribute [r] result
21
30
  # @return [ActiveRecord::Relation]
22
31
  attr_reader :params, :result
23
32
 
24
- # Set `scopes` on inheritance to ensure they're unique per subclass
33
+ # Set `mappings` on inheritance to ensure they're unique per subclass
25
34
  def self.inherited(klass)
26
- klass.scopes = []
27
- end
35
+ klass.mappings = []
36
+ klass.source = klass.to_s.sub(/Query$/, '')
28
37
 
29
- # Specify the `ActiveRecord` source class if not determinable from the name
30
- # of the `Paraphrase::Query` subclass.
31
- #
32
- # @param [String, Symbol] name name of the source class
33
- def self.source(name)
34
- self._source = name.to_s
38
+ klass.params_filter = Class.new(Paraphrase::ParamsFilter)
39
+ klass.const_set(:ParamsFilter, klass.params_filter)
40
+
41
+ klass.repository = Class.new(Paraphrase::Repository)
42
+ klass.const_set(:Repository, klass.repository)
35
43
  end
36
44
 
37
45
  # Keys being mapped to scopes
38
46
  #
39
47
  # @return [Array<Symbol>]
40
48
  def self.keys
41
- scopes.flat_map(&:keys)
49
+ mappings.flat_map(&:keys)
42
50
  end
43
51
 
44
- # Returns the class for processing and filtering query params.
45
- def self.param_processor
46
- self::Params
47
- rescue NameError
48
- Paraphrase::Params
49
- end
50
-
51
- # Add a {Scope} instance to {Query#scopes}. Defines a reader for each key
52
- # to read from {Query#params}.
52
+ # Add a {Mapping} instance to {Query#mappings}. Defines a reader for each
53
+ # key to read from {Query#params}.
53
54
  #
54
- # @see Scope#initialize
55
+ # @overload map(*keys, options)
56
+ # Maps a key to a scope
57
+ # @param [Array<Symbol>] keys query params to be mapped to the scope
58
+ # @param [Hash] options options to configure {Mapping Mapping} instance
59
+ # @option options [Symbol, Array<Symbol>] :to scope to map query params to
60
+ # @option options [true, Symbol, Array<Symbol>] :whitelist lists all or a
61
+ # subset of param keys as optional
55
62
  def self.map(*keys)
56
63
  options = keys.extract_options!
57
64
  scope_name = options[:to]
58
65
 
59
- if scopes.any? { |scope| scope.name == scope_name }
60
- raise DuplicateScopeError, "scope :#{scope_name} has already been mapped"
66
+ if mappings.any? { |mapping| mapping.name == scope_name }
67
+ raise DuplicateMappingError.new(scope_name)
61
68
  end
62
69
 
63
- scopes << Scope.new(keys, options)
70
+ mappings << Mapping.new(keys, options)
64
71
 
65
72
  keys.each do |key|
66
73
  define_method(key) { params[key] }
67
74
  end
68
75
  end
69
76
 
77
+ # Define a method on `ParamsFilter` to process the raw value of the query
78
+ # param
79
+ #
80
+ # @param [Symbol] query_param query param to process
81
+ # @param [Proc] block block to process the query param
82
+ def self.param(query_param, &block)
83
+ params_filter.class_eval do
84
+ define_method(query_param, &block)
85
+ end
86
+ end
87
+
88
+ # Define a scope on `Repository`
89
+ def self.scope(scope_name, &block)
90
+ repository.class_eval do
91
+ define_method(scope_name, &block)
92
+ end
93
+ end
94
+
70
95
  # Filters out parameters irrelevant to the query and sets the base scope
71
96
  # for to begin the chain.
72
97
  #
73
98
  # @param [Hash] params query parameters
74
99
  # @param [ActiveRecord::Relation] relation object to apply methods to
75
- def initialize(params = {}, relation = source)
76
- @params = process_params(params)
100
+ def initialize(query_params, relation = nil)
101
+ @params = filter_params(query_params || {})
77
102
 
78
- @result = scopes.inject(relation) do |result, scope|
79
- scope.chain(@params, result)
103
+ @result = mappings.inject(relation || default_relation) do |result, mapping|
104
+ repository.chain(result, mapping, @params)
80
105
  end
81
106
  end
82
107
 
83
108
  # Return an `ActiveRecord::Relation` corresponding to the source class
84
- # determined from the `_source` class attribute or the name of the query
85
- # class.
109
+ # determined from the `source` class attribute that defaults to the name of
110
+ # the class.
86
111
  #
87
112
  # @return [ActiveRecord::Relation]
88
- def source
89
- @source ||= begin
90
- name = _source || self.class.to_s.sub(/Query$/, '')
91
- name.constantize
92
- end
113
+ def default_relation
114
+ klass = self.class.source.to_s.constantize
115
+ klass.default_paraphrase_relation
93
116
  end
94
117
 
95
118
  # @see Query.keys
@@ -99,8 +122,8 @@ module Paraphrase
99
122
 
100
123
  private
101
124
 
102
- def process_params(params)
103
- self.class.param_processor.new(params, keys).result
125
+ def filter_params(params)
126
+ params_filter.new(params, keys).result
104
127
  end
105
128
  end
106
129
  end