paraphrase 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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: