paraphrase 0.9.0 → 0.10.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.
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