paraphrase 0.2.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/.document ADDED
@@ -0,0 +1,3 @@
1
+ -
2
+ ChangeLog.md
3
+ LICENSE.txt
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ Gemfile.lock
2
+ doc/
3
+ pkg/
4
+ vendor/cache/*.gem
5
+ .yardoc
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --colour
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ --markup markdown --title "paraphrase Documentation" --protected -M github-markup -M redcarpet
data/Appraisals ADDED
@@ -0,0 +1,17 @@
1
+ appraise '3.0' do
2
+ gem 'activerecord', '3.0.15'
3
+ gem 'activemodel', '3.0.15'
4
+ gem 'activesupport', '3.0.15'
5
+ end
6
+
7
+ appraise '3.1' do
8
+ gem 'activerecord', '3.1.6'
9
+ gem 'activemodel', '3.1.6'
10
+ gem 'activesupport', '3.1.6'
11
+ end
12
+
13
+ appraise '3.2' do
14
+ gem 'activerecord', '3.2.6'
15
+ gem 'activemodel', '3.2.6'
16
+ gem 'activesupport', '3.2.6'
17
+ end
data/ChangeLog.md ADDED
@@ -0,0 +1,4 @@
1
+ ### 0.1.0 / 2012-06-04
2
+
3
+ * Initial release:
4
+
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source :rubygems
2
+
3
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Eduardo Gutierrez
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.md ADDED
@@ -0,0 +1,134 @@
1
+ # paraphrase
2
+
3
+ paraphrase provides a way to map one or multiple request params to model
4
+ scopes.
5
+
6
+ paraphrase was designed and geared towards building a query-based public API
7
+ where you may want to require certain parameters to prevent consumers from
8
+ scraping all your information or to mitigate the possibility of large,
9
+ performance-intensive data-dumps.
10
+
11
+ ## Installation
12
+
13
+ Via a gemfile:
14
+
15
+ ```ruby
16
+ gem 'paraphrase'
17
+ ```
18
+
19
+ ```
20
+ $ bundle
21
+ ```
22
+
23
+ Or manually:
24
+
25
+ ```
26
+ $ gem install paraphrase
27
+ ```
28
+
29
+ ## Usage
30
+
31
+ paraphrase aims to be as flexible as possible for your needs.
32
+ * From within an `ActiveRecord::Base` subclass:
33
+
34
+ ```ruby
35
+ class Post < ActiveRecord::Base
36
+ register_mapping do
37
+ scope :by_user, :key => :author
38
+ end
39
+
40
+ def self.by_user(author_name)
41
+ joins(:user).where(:user => { :name => author_name })
42
+ end
43
+ end
44
+ ```
45
+
46
+ * In an initializer to register multiple mappings in one place:
47
+
48
+ ```ruby
49
+ # config/initializers/paraphrase.rb
50
+
51
+ Paraphrase.configure do |mappings|
52
+ mappings.register :post do
53
+ paraphrases Post
54
+ scope :by_user, :key => :author
55
+ end
56
+ end
57
+ ```
58
+
59
+ * By creating a subclass of `Paraphrase::Query`:
60
+
61
+ ```ruby
62
+ class PostQuery < Paraphrase::Query
63
+ paraphrases Post
64
+
65
+ scope :by_user, :key => :author
66
+ end
67
+ ```
68
+
69
+ Then in a controller you can use it in any of the following ways:
70
+
71
+ ```ruby
72
+ class PostsController < ApplicationController
73
+ respond_to :html, :json
74
+
75
+ def index
76
+ # Filters out relevant attributes
77
+ # and applies scopes relevant to each
78
+ # parameter
79
+ @posts = Post.paraphrase(params)
80
+
81
+ # Or
82
+ # @posts = Paraphrase.query(:post, params)
83
+
84
+ # If you created a subclass
85
+ # @posts = PostQuery.new(params)
86
+
87
+ respond_with(@posts)
88
+ end
89
+ end
90
+ ```
91
+
92
+ In any of these contexts, the `:key` option of the `:scope` method registers
93
+ attribute(s) to extract from the params supplied and what scope to pass them
94
+ to. An array of keys can be supplied to pass multiple attributes to a scope.
95
+
96
+ ```ruby
97
+ class Post < ActiveRecord::Base
98
+ register_mapping do
99
+ scope :by_user, :key => [:first_name, :last_name]
100
+ end
101
+
102
+ def self.by_user(name)
103
+ joins(:user).where(:user => { :name => name })
104
+ end
105
+ end
106
+ ```
107
+
108
+ If a key is required, pass `:require => true` to the options. This will
109
+ return an empty results set if value for that key is missing.
110
+
111
+ ```ruby
112
+ class Post < ActiveRecord::Base
113
+ register_mapping do
114
+ scope :by_author, :key => :author, :require => true
115
+ scope :published_after, :key => :pub_date
116
+ end
117
+ end
118
+
119
+ Post.paraphrase(:pub_date => '2010-10-30') # => []
120
+ ```
121
+
122
+ ## Plans
123
+
124
+ * Enable requiring a subset of a compound key.
125
+
126
+ ```ruby
127
+ scope :by_author, :key => [:first_name, :last_name], :require => :first_name
128
+ ```
129
+
130
+ * Support nested hashes in params.
131
+
132
+ ```ruby
133
+ scope :by_author, :key => { :author => [:first_name, :last_name] }
134
+ ```
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env rake
2
+ require 'bundler/gem_tasks'
3
+ require 'bundler/setup'
4
+ require 'rspec/core/rake_task'
5
+ require 'appraisal'
6
+
7
+ desc "Run specs"
8
+ RSpec::Core::RakeTask.new
9
+
10
+ task :test => :spec
11
+ task :default => :spec
@@ -0,0 +1,9 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source :rubygems
4
+
5
+ gem "activerecord", "3.0.15"
6
+ gem "activemodel", "3.0.15"
7
+ gem "activesupport", "3.0.15"
8
+
9
+ gemspec :path=>"../"
@@ -0,0 +1,57 @@
1
+ PATH
2
+ remote: /Users/edd_d/src/paraphrase
3
+ specs:
4
+ paraphrase (0.1.0)
5
+ activemodel (~> 3.0)
6
+ activerecord (~> 3.0)
7
+ activesupport (~> 3.0)
8
+
9
+ GEM
10
+ remote: http://rubygems.org/
11
+ specs:
12
+ activemodel (3.0.15)
13
+ activesupport (= 3.0.15)
14
+ builder (~> 2.1.2)
15
+ i18n (~> 0.5.0)
16
+ activerecord (3.0.15)
17
+ activemodel (= 3.0.15)
18
+ activesupport (= 3.0.15)
19
+ arel (~> 2.0.10)
20
+ tzinfo (~> 0.3.23)
21
+ activesupport (3.0.15)
22
+ appraisal (0.4.1)
23
+ bundler
24
+ rake
25
+ arel (2.0.10)
26
+ builder (2.1.2)
27
+ diff-lcs (1.1.3)
28
+ i18n (0.5.0)
29
+ rake (0.9.2.2)
30
+ redcarpet (2.1.1)
31
+ rspec (2.10.0)
32
+ rspec-core (~> 2.10.0)
33
+ rspec-expectations (~> 2.10.0)
34
+ rspec-mocks (~> 2.10.0)
35
+ rspec-core (2.10.1)
36
+ rspec-expectations (2.10.0)
37
+ diff-lcs (~> 1.1.3)
38
+ rspec-mocks (2.10.1)
39
+ sqlite3 (1.3.6)
40
+ tzinfo (0.3.33)
41
+ yard (0.8.2.1)
42
+
43
+ PLATFORMS
44
+ ruby
45
+
46
+ DEPENDENCIES
47
+ activemodel (= 3.0.15)
48
+ activerecord (= 3.0.15)
49
+ activesupport (= 3.0.15)
50
+ appraisal
51
+ bundler (~> 1.0)
52
+ paraphrase!
53
+ rake
54
+ redcarpet
55
+ rspec (~> 2.10)
56
+ sqlite3
57
+ yard (~> 0.7)
@@ -0,0 +1,9 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source :rubygems
4
+
5
+ gem "activerecord", "3.1.6"
6
+ gem "activemodel", "3.1.6"
7
+ gem "activesupport", "3.1.6"
8
+
9
+ gemspec :path=>"../"
@@ -0,0 +1,59 @@
1
+ PATH
2
+ remote: /Users/edd_d/src/paraphrase
3
+ specs:
4
+ paraphrase (0.1.0)
5
+ activemodel (~> 3.0)
6
+ activerecord (~> 3.0)
7
+ activesupport (~> 3.0)
8
+
9
+ GEM
10
+ remote: http://rubygems.org/
11
+ specs:
12
+ activemodel (3.1.6)
13
+ activesupport (= 3.1.6)
14
+ builder (~> 3.0.0)
15
+ i18n (~> 0.6)
16
+ activerecord (3.1.6)
17
+ activemodel (= 3.1.6)
18
+ activesupport (= 3.1.6)
19
+ arel (~> 2.2.3)
20
+ tzinfo (~> 0.3.29)
21
+ activesupport (3.1.6)
22
+ multi_json (>= 1.0, < 1.3)
23
+ appraisal (0.4.1)
24
+ bundler
25
+ rake
26
+ arel (2.2.3)
27
+ builder (3.0.0)
28
+ diff-lcs (1.1.3)
29
+ i18n (0.6.0)
30
+ multi_json (1.2.0)
31
+ rake (0.9.2.2)
32
+ redcarpet (2.1.1)
33
+ rspec (2.10.0)
34
+ rspec-core (~> 2.10.0)
35
+ rspec-expectations (~> 2.10.0)
36
+ rspec-mocks (~> 2.10.0)
37
+ rspec-core (2.10.1)
38
+ rspec-expectations (2.10.0)
39
+ diff-lcs (~> 1.1.3)
40
+ rspec-mocks (2.10.1)
41
+ sqlite3 (1.3.6)
42
+ tzinfo (0.3.33)
43
+ yard (0.8.2.1)
44
+
45
+ PLATFORMS
46
+ ruby
47
+
48
+ DEPENDENCIES
49
+ activemodel (= 3.1.6)
50
+ activerecord (= 3.1.6)
51
+ activesupport (= 3.1.6)
52
+ appraisal
53
+ bundler (~> 1.0)
54
+ paraphrase!
55
+ rake
56
+ redcarpet
57
+ rspec (~> 2.10)
58
+ sqlite3
59
+ yard (~> 0.7)
@@ -0,0 +1,9 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source :rubygems
4
+
5
+ gem "activerecord", "3.2.6"
6
+ gem "activemodel", "3.2.6"
7
+ gem "activesupport", "3.2.6"
8
+
9
+ gemspec :path=>"../"
@@ -0,0 +1,59 @@
1
+ PATH
2
+ remote: /Users/edd_d/src/paraphrase
3
+ specs:
4
+ paraphrase (0.1.0)
5
+ activemodel (~> 3.0)
6
+ activerecord (~> 3.0)
7
+ activesupport (~> 3.0)
8
+
9
+ GEM
10
+ remote: http://rubygems.org/
11
+ specs:
12
+ activemodel (3.2.6)
13
+ activesupport (= 3.2.6)
14
+ builder (~> 3.0.0)
15
+ activerecord (3.2.6)
16
+ activemodel (= 3.2.6)
17
+ activesupport (= 3.2.6)
18
+ arel (~> 3.0.2)
19
+ tzinfo (~> 0.3.29)
20
+ activesupport (3.2.6)
21
+ i18n (~> 0.6)
22
+ multi_json (~> 1.0)
23
+ appraisal (0.4.1)
24
+ bundler
25
+ rake
26
+ arel (3.0.2)
27
+ builder (3.0.0)
28
+ diff-lcs (1.1.3)
29
+ i18n (0.6.0)
30
+ multi_json (1.3.6)
31
+ rake (0.9.2.2)
32
+ redcarpet (2.1.1)
33
+ rspec (2.10.0)
34
+ rspec-core (~> 2.10.0)
35
+ rspec-expectations (~> 2.10.0)
36
+ rspec-mocks (~> 2.10.0)
37
+ rspec-core (2.10.1)
38
+ rspec-expectations (2.10.0)
39
+ diff-lcs (~> 1.1.3)
40
+ rspec-mocks (2.10.1)
41
+ sqlite3 (1.3.6)
42
+ tzinfo (0.3.33)
43
+ yard (0.8.2.1)
44
+
45
+ PLATFORMS
46
+ ruby
47
+
48
+ DEPENDENCIES
49
+ activemodel (= 3.2.6)
50
+ activerecord (= 3.2.6)
51
+ activesupport (= 3.2.6)
52
+ appraisal
53
+ bundler (~> 1.0)
54
+ paraphrase!
55
+ rake
56
+ redcarpet
57
+ rspec (~> 2.10)
58
+ sqlite3
59
+ yard (~> 0.7)
@@ -0,0 +1,4 @@
1
+ module Paraphrase
2
+ class DuplicateMappingError < StandardError; end
3
+ class DuplicateScopeError < StandardError; end
4
+ end
@@ -0,0 +1,86 @@
1
+ require 'active_support/core_ext/class/attribute_accessors'
2
+ require 'active_support/core_ext/class/attribute'
3
+ require 'active_support/core_ext/module/delegation'
4
+ require 'active_model/naming'
5
+ require 'active_model/errors'
6
+
7
+ module Paraphrase
8
+ class Query
9
+ extend ActiveModel::Naming
10
+
11
+ # @!attribute [r] scopes
12
+ # @return [Array<ScopeMapping>] scopes for query
13
+ #
14
+ # @!attribute [r] source
15
+ # @return [ActiveRecord::Relation] source to apply scopes to
16
+ cattr_reader :scopes, :source
17
+ @@scopes = []
18
+
19
+
20
+ # Delegate enumerable methods to results
21
+ delegate :collect, :map, :each, :select, :to_a, :to_ary, :to => :results
22
+
23
+
24
+ # @!attribute [r] errors
25
+ # @return [ActiveModel::Errors] errors from determining results
26
+ #
27
+ # @!attribute [r] params
28
+ # @return [Hash] filters parameters based on keys defined in scopes
29
+ attr_reader :errors, :params
30
+
31
+
32
+ # Specify the ActiveRecord model to use as the source for queries
33
+ #
34
+ # @param [String, Symbol, ActiveRecord::Base] klass name of the class to
35
+ # use or the class itself
36
+ def self.paraphrases(klass)
37
+ if !klass.is_a?(Class)
38
+ klass = Object.const_get(klass.to_s.classify)
39
+ end
40
+
41
+ @@source = klass.scoped
42
+
43
+ Paraphrase.add(klass.name, self)
44
+ end
45
+
46
+
47
+ # Add a {ScopeMapping} instance to {@@scopes .scopes}
48
+ #
49
+ # @see ScopeMapping#initialize
50
+ def self.scope(name, options)
51
+ if @@scopes.map(&:method_name).include?(name)
52
+ raise DuplicateScopeError, "scope :#{name} has already been added"
53
+ end
54
+
55
+ @@scopes << ScopeMapping.new(name, options)
56
+ end
57
+
58
+
59
+ # Filters out parameters irrelevant to the query
60
+ #
61
+ # @param [Hash] params query parameters
62
+ def initialize(params = {})
63
+ keys = scopes.map(&:param_keys).flatten
64
+ @params = params.dup
65
+ @params.select! { |key, value| keys.include?(key) }
66
+ @params.freeze
67
+
68
+ @errors = ActiveModel::Errors.new(self)
69
+ end
70
+
71
+
72
+ # Loops through {#scopes} and apply scope methods to {#source}. If values
73
+ # are missing for a required key, an empty array is returned.
74
+ #
75
+ # @return [ActiveRecord::Relation, Array]
76
+ def results
77
+ results ||= scopes.inject(source) do |query, scope|
78
+ scope.chain(self, @params, query)
79
+ end
80
+
81
+ @results = @errors.any? ? [] : results
82
+ end
83
+ end
84
+ end
85
+
86
+ require 'paraphrase/scope_mapping'
@@ -0,0 +1,11 @@
1
+ require 'rails'
2
+
3
+ module Paraphrase
4
+ class Railtie < Rails::Railtie
5
+ initializer 'paraphrase.extend_active_record' do
6
+ ActiveSupport.on_load :active_record do
7
+ extend Paraphrase::Syntax
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,55 @@
1
+ module Paraphrase
2
+ class ScopeMapping
3
+ # @!attribute [r] param_keys
4
+ # @return [Array<Symbol>] param keys to extract
5
+ #
6
+ # @!attribute [r] method_name
7
+ # @return [Symbol] scope method name
8
+ #
9
+ # @!attribute [r] options
10
+ # @return [Hash] configuration options
11
+ attr_reader :param_keys, :method_name, :options
12
+
13
+
14
+ # @param [Symbol] name name of the scope
15
+ # @param [Hash] options options to configure {ScopeMapping ScopeMapping} instance
16
+ # @option options [Symbol, Array<Symbol>] :key param key(s) to extract values from
17
+ # @option options [true] :require lists scope as required
18
+ def initialize(name, options)
19
+ @method_name = name
20
+ @param_keys = [options.delete(:key)].flatten
21
+
22
+ @options = options.freeze
23
+ end
24
+
25
+
26
+ # Checks if scope is required for query
27
+ def required?
28
+ !options[:require].nil?
29
+ end
30
+
31
+
32
+ # Sends {#method_name} to `chain`, extracting arguments from `params`. If
33
+ # values are missing for any {#param_keys}, return the `chain` unmodified.
34
+ # If {#required? required}, errors are added to the {Query} instance as
35
+ # well.
36
+ #
37
+ # @param [Query] query {Query} instance applying the scope
38
+ # @param [Hash] params hash of query parameters
39
+ # @param [ActiveRecord::Relation, ActiveRecord::Base] chain current model scope
40
+ # @return [ActiveRecord::Relation]
41
+ def chain(query, params, chain)
42
+ inputs = param_keys.map { |key| params[key] }
43
+
44
+ if inputs.include?(nil)
45
+ param_keys.each do |key|
46
+ query.errors.add(key, 'is required')
47
+ end if required?
48
+
49
+ chain
50
+ else
51
+ chain.send(method_name, *inputs)
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,19 @@
1
+ module Paraphrase
2
+ module Syntax
3
+
4
+ # Register a {Query} class mapped to `self`.
5
+ #
6
+ # @param [Proc] &block block to define scope mappings
7
+ def register_mapping(&block)
8
+ Paraphrase.register(self.name, &block)
9
+ end
10
+
11
+
12
+ # Instantiate the {Query} class that is mapped to `self`.
13
+ #
14
+ # @param [Hash] params query parameters
15
+ def paraphrase(params)
16
+ Paraphrase.query(self.name.underscore, params)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,3 @@
1
+ module Paraphrase
2
+ VERSION = "0.2.0"
3
+ end
data/lib/paraphrase.rb ADDED
@@ -0,0 +1,65 @@
1
+ require 'active_support/core_ext/module/attribute_accessors'
2
+ require 'active_support/core_ext/hash/indifferent_access'
3
+ require 'active_support/core_ext/string/inflections'
4
+
5
+ module Paraphrase
6
+
7
+ @@mappings = {}.with_indifferent_access
8
+
9
+ # Allows for configuring multiple {Query} classes in a single block. Useful
10
+ # in an initializer.
11
+ def self.configure(&block)
12
+ yield self
13
+ end
14
+
15
+
16
+ # Retreive a registered Query subclass.
17
+ #
18
+ # @param [String, Symbol] name of the class underscored
19
+ # @return [Query]
20
+ def self.mapping(name)
21
+ @@mappings[name]
22
+ end
23
+
24
+
25
+ # Add a new subclass of Paraprhase::Query. The provided block is evaluated in
26
+ # the context of a Query subclass to define scope mappings.
27
+ #
28
+ # @param [String, Symbol] name name of the model in any inflector form
29
+ # @param [Proc] block defining mappings of scopes to keys for Query subclass
30
+ def self.register(name, &block)
31
+ klass = Class.new(Query, &block)
32
+ klass.paraphrases(name.to_s.classify)
33
+ end
34
+
35
+
36
+ # Register a subclass of Paraphrase::Query. Useful for manually subclassing
37
+ # Paraphrase::Query to add custom functionality.
38
+ #
39
+ # @param [String, Symbol] name name of the class in any ActiveSupport inflector form
40
+ # @param [Query] klass subclass of Paraphrase::Query
41
+ def self.add(name, klass)
42
+ name = name.to_s.underscore
43
+
44
+ if @@mappings[name]
45
+ raise DuplicateMappingError, "#{name.classify} has already been added"
46
+ end
47
+
48
+ @@mappings[name] = klass
49
+ end
50
+
51
+
52
+ # Instantiate a new Query subclass using supplied params
53
+ #
54
+ # @param [String, Symbol] name name of the model in underscored form
55
+ # @param [Hash] params hash of query params
56
+ # @return [Query]
57
+ def self.query(name, params)
58
+ @@mappings[name].new(params)
59
+ end
60
+ end
61
+
62
+ require 'paraphrase/errors'
63
+ require 'paraphrase/query'
64
+ require 'paraphrase/syntax'
65
+ require 'paraphrase/rails' if defined?(Rails)
@@ -0,0 +1,31 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require File.expand_path('../lib/paraphrase/version', __FILE__)
4
+
5
+ Gem::Specification.new do |gem|
6
+ gem.name = "paraphrase"
7
+ gem.version = Paraphrase::VERSION
8
+ gem.summary = %q{Map param keys to class scopes}
9
+ gem.description = %q{Map param keys to class scopes}
10
+ gem.license = "MIT"
11
+ gem.authors = ["Eduardo Gutierrez"]
12
+ gem.email = "edd_d@mit.edu"
13
+ gem.homepage = "https://rubygems.org/gems/paraphrase"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ['lib']
19
+
20
+ gem.add_dependency 'activerecord', '~> 3.0'
21
+ gem.add_dependency 'activesupport', '~> 3.0'
22
+ gem.add_dependency 'activemodel', '~> 3.0'
23
+
24
+ gem.add_development_dependency 'bundler', '~> 1.0'
25
+ gem.add_development_dependency 'yard', '~> 0.7'
26
+ gem.add_development_dependency 'rspec', '~> 2.10'
27
+ gem.add_development_dependency 'redcarpet'
28
+ gem.add_development_dependency 'rake'
29
+ gem.add_development_dependency 'sqlite3'
30
+ gem.add_development_dependency 'appraisal'
31
+ end
@@ -0,0 +1,62 @@
1
+ require 'spec_helper'
2
+
3
+ module Paraphrase
4
+ describe Query do
5
+
6
+ describe ".paraphrases" do
7
+ it "stores the class being queried" do
8
+ UserSearch.paraphrases :user
9
+ UserSearch.source.should eq User.scoped
10
+ end
11
+
12
+ it "registers the query in Paraphrase.querys" do
13
+ Paraphrase.mapping(:user).should eq UserSearch
14
+ end
15
+ end
16
+
17
+ describe ".scope" do
18
+ it "adds information to Query.scopes" do
19
+ UserSearch.instance_eval do
20
+ scope :name_like, :key => :name
21
+ end
22
+
23
+ UserSearch.scopes.should_not be_empty
24
+ end
25
+
26
+ it "raises an error if a scope is added twice" do
27
+ expect { UserSearch.instance_eval { scope :name_like, :key => :name } }.to raise_error Paraphrase::DuplicateScopeError
28
+ end
29
+ end
30
+
31
+ describe "#initialize" do
32
+ it "filters out params not specified in scopes" do
33
+ query = UserSearch.new(:name => 'Tyrion Lannister', :nickname => 'Half Man')
34
+
35
+ query.params.should_not have_key :nickname
36
+ query.params.should have_key :name
37
+ end
38
+ end
39
+
40
+ describe "#results" do
41
+ before :all do
42
+ UserSearch.instance_eval do
43
+ scope :title_like, :key => :title, :require => true
44
+ end
45
+ end
46
+
47
+ it "loops through scope methods and applies them to source" do
48
+ User.should_receive(:title_like).and_return(User.scoped)
49
+ User.should_receive(:name_like).and_return(User.scoped)
50
+
51
+ query = UserSearch.new(:name => 'Jon Snow', :title => 'Wall Watcher')
52
+ query.results
53
+ end
54
+
55
+ it "returns empty array if errors were added" do
56
+ query = UserSearch.new
57
+ query.results.should eq []
58
+ query.errors.should_not be_empty
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+
3
+ module Paraphrase
4
+ describe ScopeMapping do
5
+ let(:scope_mapping) do
6
+ ScopeMapping.new :name_like, :key => :name
7
+ end
8
+
9
+ it "removes keys from options" do
10
+ scope_mapping.options.should_not have_key :key
11
+ end
12
+
13
+ describe "#chain" do
14
+ let(:query) { double('query') }
15
+
16
+ it "applies scope method to query object with values from params hash" do
17
+ Account.should_receive(:name_like).with('Jon Snow')
18
+ scope_mapping.chain(query, { :name => 'Jon Snow' }, Account)
19
+ end
20
+
21
+ it "does nothing if values are missing" do
22
+ Account.should_not_receive(:name_like).with('Jon Snow')
23
+ scope_mapping.chain(query, {}, Account)
24
+ end
25
+
26
+ it "adds errors to query object if missing and required" do
27
+ errors = double('errors')
28
+ query.stub(:errors).and_return(errors)
29
+ required_mapping = ScopeMapping.new :name_like, :key => :name, :require => true
30
+
31
+ errors.should_receive(:add)
32
+ required_mapping.chain(query, {}, Account)
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ module Paraphrase
4
+ describe Syntax do
5
+ describe ".register_mapping" do
6
+ it "forwards to Paraphrase.register" do
7
+ ::Account.register_mapping {}
8
+ Paraphrase.mapping(:account).should_not be_nil
9
+ end
10
+ end
11
+
12
+ describe ".paraphrase" do
13
+ it "forwards to Paraphrase.query" do
14
+ Paraphrase.should_receive(:query).with('account', {})
15
+ ::Account.paraphrase({})
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+
3
+ describe Paraphrase do
4
+
5
+ describe ".configure" do
6
+ it "is a convenience method for configuring multiple query classes" do
7
+ Paraphrase.configure do |mapping|
8
+ mapping.register(:person) {}
9
+ end
10
+
11
+ Paraphrase.mapping(:person).should_not be_nil
12
+ end
13
+ end
14
+
15
+ describe ".register" do
16
+ it "a sublcass of Paraphrase::Query to @@mappings" do
17
+ Paraphrase.register(:foobar) {}
18
+ Paraphrase.mapping(:foobar).should_not be_nil
19
+ end
20
+
21
+ it "adds the source to the new subclass" do
22
+ Paraphrase.mapping(:foobar).source.should eq Foobar.scoped
23
+ end
24
+
25
+ it "raises an error if mappings for a class are added twice" do
26
+ expect { Paraphrase.register(:foobar) {} }.to raise_error Paraphrase::DuplicateMappingError
27
+ end
28
+ end
29
+
30
+ describe ".query" do
31
+ it "instantiates a new Query class" do
32
+ Paraphrase.query(:foobar, {}).should be_a Paraphrase::Query
33
+ end
34
+ end
35
+
36
+ describe ".add" do
37
+ it "adds class to mapping with specified name" do
38
+ klass = Class.new(Paraphrase::Query)
39
+ Paraphrase.add(:baz, klass)
40
+ Paraphrase.mapping(:baz).should eq klass
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,35 @@
1
+ require 'rspec'
2
+ require 'paraphrase'
3
+ require 'active_record'
4
+
5
+ ActiveRecord::Base.establish_connection(
6
+ :adapter => 'sqlite3',
7
+ :database => ':memory:'
8
+ )
9
+
10
+ ActiveRecord::Base.silence do
11
+ ActiveRecord::Migration.verbose = false
12
+
13
+ ActiveRecord::Schema.define do
14
+ create_table(:users, :force => true) {}
15
+ create_table(:accounts, :force => true) {}
16
+ create_table(:foobars, :force => true) {}
17
+ create_table(:people, :force => true) {}
18
+ end
19
+ end
20
+
21
+ class Person < ActiveRecord::Base
22
+ end
23
+
24
+ class Foobar < ActiveRecord::Base
25
+ end
26
+
27
+ class Account < ActiveRecord::Base
28
+ extend Paraphrase::Syntax
29
+ end
30
+
31
+ class User < ActiveRecord::Base
32
+ end
33
+
34
+ class UserSearch < Paraphrase::Query
35
+ end
metadata ADDED
@@ -0,0 +1,190 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: paraphrase
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Eduardo Gutierrez
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-06-23 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activerecord
16
+ requirement: &70178168974020 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '3.0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70178168974020
25
+ - !ruby/object:Gem::Dependency
26
+ name: activesupport
27
+ requirement: &70178168972120 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: '3.0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70178168972120
36
+ - !ruby/object:Gem::Dependency
37
+ name: activemodel
38
+ requirement: &70178168971600 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: '3.0'
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *70178168971600
47
+ - !ruby/object:Gem::Dependency
48
+ name: bundler
49
+ requirement: &70178168971060 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '1.0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *70178168971060
58
+ - !ruby/object:Gem::Dependency
59
+ name: yard
60
+ requirement: &70178168970400 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ~>
64
+ - !ruby/object:Gem::Version
65
+ version: '0.7'
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *70178168970400
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: &70178168969700 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ~>
75
+ - !ruby/object:Gem::Version
76
+ version: '2.10'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: *70178168969700
80
+ - !ruby/object:Gem::Dependency
81
+ name: redcarpet
82
+ requirement: &70178168969060 !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: *70178168969060
91
+ - !ruby/object:Gem::Dependency
92
+ name: rake
93
+ requirement: &70178168968360 !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ! '>='
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ type: :development
100
+ prerelease: false
101
+ version_requirements: *70178168968360
102
+ - !ruby/object:Gem::Dependency
103
+ name: sqlite3
104
+ requirement: &70178168967600 !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: *70178168967600
113
+ - !ruby/object:Gem::Dependency
114
+ name: appraisal
115
+ requirement: &70178168966640 !ruby/object:Gem::Requirement
116
+ none: false
117
+ requirements:
118
+ - - ! '>='
119
+ - !ruby/object:Gem::Version
120
+ version: '0'
121
+ type: :development
122
+ prerelease: false
123
+ version_requirements: *70178168966640
124
+ description: Map param keys to class scopes
125
+ email: edd_d@mit.edu
126
+ executables: []
127
+ extensions: []
128
+ extra_rdoc_files: []
129
+ files:
130
+ - .document
131
+ - .gitignore
132
+ - .rspec
133
+ - .yardopts
134
+ - Appraisals
135
+ - ChangeLog.md
136
+ - Gemfile
137
+ - LICENSE.txt
138
+ - README.md
139
+ - Rakefile
140
+ - gemfiles/3.0.gemfile
141
+ - gemfiles/3.0.gemfile.lock
142
+ - gemfiles/3.1.gemfile
143
+ - gemfiles/3.1.gemfile.lock
144
+ - gemfiles/3.2.gemfile
145
+ - gemfiles/3.2.gemfile.lock
146
+ - lib/paraphrase.rb
147
+ - lib/paraphrase/errors.rb
148
+ - lib/paraphrase/query.rb
149
+ - lib/paraphrase/rails.rb
150
+ - lib/paraphrase/scope_mapping.rb
151
+ - lib/paraphrase/syntax.rb
152
+ - lib/paraphrase/version.rb
153
+ - paraphrase.gemspec
154
+ - spec/paraphrase/query_spec.rb
155
+ - spec/paraphrase/scope_mapping_spec.rb
156
+ - spec/paraphrase/syntax_spec.rb
157
+ - spec/paraphrase_spec.rb
158
+ - spec/spec_helper.rb
159
+ homepage: https://rubygems.org/gems/paraphrase
160
+ licenses:
161
+ - MIT
162
+ post_install_message:
163
+ rdoc_options: []
164
+ require_paths:
165
+ - lib
166
+ required_ruby_version: !ruby/object:Gem::Requirement
167
+ none: false
168
+ requirements:
169
+ - - ! '>='
170
+ - !ruby/object:Gem::Version
171
+ version: '0'
172
+ required_rubygems_version: !ruby/object:Gem::Requirement
173
+ none: false
174
+ requirements:
175
+ - - ! '>='
176
+ - !ruby/object:Gem::Version
177
+ version: '0'
178
+ requirements: []
179
+ rubyforge_project:
180
+ rubygems_version: 1.8.11
181
+ signing_key:
182
+ specification_version: 3
183
+ summary: Map param keys to class scopes
184
+ test_files:
185
+ - spec/paraphrase/query_spec.rb
186
+ - spec/paraphrase/scope_mapping_spec.rb
187
+ - spec/paraphrase/syntax_spec.rb
188
+ - spec/paraphrase_spec.rb
189
+ - spec/spec_helper.rb
190
+ has_rdoc: