parascope 0.1.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 20fc5771d922f27cb3562954c138dd4d9069f01a
4
+ data.tar.gz: 2da5ec9511b1cdea10f7a7e84aaa6023c358733c
5
+ SHA512:
6
+ metadata.gz: af2fce7745657960a666ae9d160b6fced85be97a3698f8b6cd1ed0d8f5b14d694e349222c011752071501e77f86a8ca62b415c2c22c4e5d75d3fc5648460a571
7
+ data.tar.gz: ab35b12aa2b8a822dd53eb113867b63fc6cd1c00685021108273c724e4569dd78267b58b4d8da395a5c7c3db8157b951a91a6efc7d4e8b2f2636bccd5c732533
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+
11
+ .ruby-gemset
12
+ .ruby-version
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.0
4
+ before_install: gem install bundler -v 1.11.2
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Artem Kuzko
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,173 @@
1
+ # Parascope
2
+
3
+ [![Build Status](https://secure.travis-ci.org/akuzko/parascope.png)](http://travis-ci.org/akuzko/parascope)
4
+
5
+ Because `periscope` is already taken.
6
+
7
+ --
8
+
9
+ This gem provides a `Parascope::Query` class with a declarative and convenient API
10
+ to build scopes (ActiveRecord relations or arbitrary objects) dynamically, based
11
+ on parameters passed to query object on initialization.
12
+
13
+ ## Installation
14
+
15
+ Add this line to your application's Gemfile:
16
+
17
+ ```ruby
18
+ gem 'parascope'
19
+ ```
20
+
21
+ And then execute:
22
+
23
+ $ bundle
24
+
25
+ Or install it yourself as:
26
+
27
+ $ gem install parascope
28
+
29
+ ## Usage
30
+
31
+ Despite the fact `parsacope` was intended to help building ActiveRecord relations
32
+ via scopes or query methods, it's usage is not limited to ActiveRecord cases and
33
+ may be used with any arbitrary classes and objects. In fact, the only gem's dependency
34
+ is `hashie`, and for development and testing, `OpenStruct` instance is used as a
35
+ generic scope object. However, ActiveRecord examples should illustrate gem's usage
36
+ in the best way.
37
+
38
+ ### API
39
+
40
+ `parascope` provides `Parascope::Query` class, descendants of which should declare
41
+ scope manipulations using `query_by`, `sift_by` and other class methods bellow.
42
+
43
+ #### Class Methods
44
+
45
+ - `query_by(*presence_fields, **value_fields, &block)` declares a scope-generation query
46
+ block that will be executed if, and only if all values of query params at the keys of
47
+ `presence_fields` are present in activesupport's definition of presence and all value
48
+ fields are present in query params as is. The block is executed in context of query
49
+ object. All values of specified params are yielded to the block. If the block
50
+ returns a non-nil value, it becomes a new scope for following processing. Of course,
51
+ there can be multiple `query_by` block definitions. Optionally, `:index` option
52
+ may be passed to control query blocks application order.
53
+
54
+ - `sift_by(*presence_fields, **value_fields, &block)` method is used to hoist sets of
55
+ query definitions that should be applied if, and only if, all specified values
56
+ match criteria in the same way as in `query_by` method. Just like `query_by` method,
57
+ values of specified fields are yielded to the block. Such `sift_by` definitions
58
+ may be nested in any depth.
59
+
60
+ - `base_scope(&block)` method is used to define a base scope as a starting point
61
+ of scope-generating process. If this method is called from `sift_by` block,
62
+ top-level base scope is yielded to the method block. Note that `base_scope` will
63
+ not be called if query is initialized with a given scope.
64
+
65
+ - `defaults(hash)` method is used to declare default query params that are reverse
66
+ merged with params passed on query initialization. When used in `sift_by` block,
67
+ hashes are merged altogether.
68
+
69
+ - `guard(&block)` defines a guard instance method block (see instance methods
70
+ bellow). All such blocks are executed before query object resolves scope via
71
+ `resolve_scope` method.
72
+
73
+ #### Instance Methods
74
+
75
+ - `initialize(params, scope: nil, **attributes)` initializes a query with `params`,
76
+ an optional scope (that if passed, is used instead of `base_scope`). All additionally
77
+ passed options are accessible via reader methods in query blocks and elsewhere.
78
+
79
+ - `params` returns a parameters passed in initialization. Is a `Hashie::Mash` instance,
80
+ thus, values can be accessible via reader methods.
81
+
82
+ - `[](key)` delegates to query `params` for slightly easier values access.
83
+
84
+ - `scope` "current" scope of query object. For an initialized query object corresponds
85
+ to base scope. Primary usage is to call this method in `query_by` blocks and return
86
+ it's mutated version corresponding to passed `query_by` arguments.
87
+
88
+ - `guard(&block)` executes a passed `block`. If this execution returns falsy value,
89
+ `UnpermittedError` is raised. You can use this method to ensure safety of param
90
+ values interpolation to a SQL string in a `query_by` block for example.
91
+
92
+ - `resolved_scope(override_params = nil)` returns a resulting scope generated by
93
+ all queries and sifted queries that fit to query params applied to base scope.
94
+ Optionally, additional params may be passed to override the ones passed on
95
+ initialization. It's the main `Query` instance method that returns the sole
96
+ purpose of it's instances.
97
+
98
+ ### Usage example with ActiveRecord Relation as a scope
99
+
100
+ ```ruby
101
+ class UserQuery < Parascope::Query
102
+ defaults only_active: true
103
+
104
+ base_scope { company.users }
105
+
106
+ query_by :only_active do
107
+ scope.active
108
+ end
109
+
110
+ query_by :birthdate do |date|
111
+ scope.by_birtdate(date)
112
+ end
113
+
114
+ query_by :name do |name|
115
+ scope.where("CONCAT(first_name, ' ', last_name) LIKE ?", "%#{name}%")
116
+ end
117
+
118
+ sift_by :sort_column, :sort_direction do |scol, sdir|
119
+ guard { sdir.to_s.downcase.in?(%w(asc desc)) }
120
+
121
+ base_scope { |scope| scope.order(scol => sdir) }
122
+
123
+ query_by(:sort_direction, sort_column: 'name') do |sdir|
124
+ scope.reorder("CONCAT(first_name, ' ', last_name) #{sdir}")
125
+ end
126
+ end
127
+
128
+ sift_by :with_projects do
129
+ base_scope { |scope| scope.joins(:projects) }
130
+
131
+ query_by :project_name do |name|
132
+ scope.where(projects: {name: name})
133
+ end
134
+ end
135
+
136
+ def users
137
+ @users ||= resolved_scope
138
+ end
139
+
140
+ def project_users
141
+ @project_users ||= resolved_scope(with_projects: true)
142
+ end
143
+ end
144
+
145
+ params = {name: 'John', sort_column: 'name', sort_direction: 'DESC', project_name: 'ExampleApp'}
146
+
147
+ query = UserQuery.new(params, company: some_company)
148
+
149
+ query.project_users # => this is the same as:
150
+ # some_company.users
151
+ # .active
152
+ # .joins(:projects)
153
+ # .where("CONCAT(first_name, ' ', last_name) LIKE ?", "%John%")
154
+ # .where(projects: {name: 'ExampleApp'})
155
+ # .order("CONCAT(first_name, ' ', last_name) DESC")
156
+ ```
157
+
158
+ ## Development
159
+
160
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run
161
+ `rake spec` to run the tests. You can also run `bin/console` for an interactive
162
+ prompt that will allow you to experiment.
163
+
164
+ ## Contributing
165
+
166
+ Bug reports and pull requests are welcome on GitHub at https://github.com/akuzko/parascope.
167
+
168
+
169
+ ## License
170
+
171
+ The gem is available as open source under the terms of the
172
+ [MIT License](http://opensource.org/licenses/MIT).
173
+
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "parascope"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ require 'ostruct'
10
+
11
+ class Query < Parascope::Query
12
+ defaults barbak: 'barbak'
13
+
14
+ base_scope { OpenStruct.new }
15
+
16
+ query_by :foo do |foo|
17
+ guard { foo == 'foo' }
18
+
19
+ scope.tap{ scope.foo = foo }
20
+ end
21
+
22
+ query_by bar: 'bar' do
23
+ scope.tap{ scope.bar = 'bar' }
24
+ end
25
+
26
+ sift_by baz: 'baz' do |baz|
27
+ defaults bakbar: 'bakbar'
28
+
29
+ guard { upcase(baz) == 'BAZ' }
30
+
31
+ base_scope { |scope| scope.tap{ scope.baz = 'baz' } }
32
+
33
+ query_by :bak do |bak|
34
+ scope.tap{ scope.bak = bak }
35
+ end
36
+
37
+ query_by :barbak do |barbak|
38
+ scope.tap{ scope.barbak = barbak }
39
+ end
40
+
41
+ sift_by :nested_baz do |nested_baz|
42
+ query_by :bakbar do |bakbar|
43
+ scope.tap{ scope.bakbar = [baz, nested_baz, bakbar].join('-') }
44
+ end
45
+ end
46
+ end
47
+
48
+ def upcase(str)
49
+ str.upcase
50
+ end
51
+ end
52
+
53
+ q = Query.new(foo: 'foo', bar: 'bar', baz: 'baz', bak: 'bak', nested_baz: 'nb')
54
+
55
+ require "pry"
56
+ Pry.start
57
+
58
+ # require "irb"
59
+ # IRB.start
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,5 @@
1
+ require "parascope/version"
2
+
3
+ module Parascope
4
+ autoload :Query, "parascope/query"
5
+ end
@@ -0,0 +1,144 @@
1
+ require "hashie/mash"
2
+
3
+ module Parascope
4
+ class Query
5
+ autoload :ApiMethods, "parascope/query/api_methods"
6
+ autoload :ApiBlock, "parascope/query/api_block"
7
+
8
+ extend Forwardable
9
+ extend ApiMethods
10
+
11
+ UndefinedScopeError = Class.new(StandardError)
12
+ UnpermittedError = Class.new(ArgumentError)
13
+
14
+ attr_reader :params
15
+ def_delegator :params, :[]
16
+
17
+ def self.inherited(subclass)
18
+ subclass.query_blocks.replace query_blocks.dup
19
+ subclass.sift_blocks.replace sift_blocks.dup
20
+ subclass.guard_blocks.replace guard_blocks.dup
21
+ subclass.base_scope(&base_scope)
22
+ subclass.defaults defaults
23
+ end
24
+
25
+ def initialize(params, scope: nil, **attrs)
26
+ @params = Hashie::Mash.new(klass.defaults).merge(params || {})
27
+ @scope = scope unless scope.nil?
28
+ @attrs = attrs
29
+ define_attr_readers
30
+ end
31
+
32
+ def scope
33
+ @scope ||= base_scope
34
+ end
35
+
36
+ def base_scope
37
+ scope = klass.ancestors
38
+ .select{ |klass| klass < Query }
39
+ .reverse
40
+ .map(&:base_scope)
41
+ .compact
42
+ .reduce(nil){ |scope, block| instance_exec(scope, &block) }
43
+
44
+ if scope.nil?
45
+ fail UndefinedScopeError, "failed to build scope. Have you missed base_scope definition?"
46
+ end
47
+
48
+ scope
49
+ end
50
+
51
+ def resolved_scope(params = nil)
52
+ return sifted_instance.resolved_scope! if params.nil?
53
+
54
+ clone_with_params(params).resolved_scope
55
+ end
56
+
57
+ def klass
58
+ sifted? ? singleton_class : self.class
59
+ end
60
+
61
+ protected
62
+
63
+ attr_writer :scope, :params
64
+ attr_accessor :block
65
+ attr_reader :attrs
66
+
67
+ def sifted_instance
68
+ block = klass.sift_blocks.find{ |block| block.fits?(params) }
69
+
70
+ block ? sifted_instance_for(block) : self
71
+ end
72
+
73
+ def resolved_scope!
74
+ guard_all
75
+ klass.query_blocks.sort{ |a, b| a.index <=> b.index }.reduce(scope) do |scope, block|
76
+ clone_with_scope(scope, block).apply_block!.scope
77
+ end
78
+ end
79
+
80
+ def apply_block!
81
+ if block && block.fits?(params)
82
+ scope = instance_exec(*block.values_for(params), &block.block)
83
+ @scope = scope unless scope.nil?
84
+ end
85
+ self
86
+ end
87
+
88
+ def sifted!(block, query)
89
+ @attrs = query.attrs
90
+ define_attr_readers
91
+ singleton_class.query_blocks.replace query.klass.query_blocks.dup
92
+ singleton_class.guard_blocks.replace query.klass.guard_blocks.dup
93
+ singleton_class.base_scope(&query.klass.base_scope)
94
+ singleton_class.instance_exec(*block.values_for(params), &block.block)
95
+ params.replace(singleton_class.defaults.merge(params))
96
+ @sifted = true
97
+ end
98
+
99
+ private
100
+
101
+ def guard_all
102
+ klass.guard_blocks.each{ |block| guard(&block) }
103
+ end
104
+
105
+ def guard(&block)
106
+ unless instance_exec(&block)
107
+ fail UnpermittedError, "processing is not allowed by guard block\non #{block.source_location.join(':')}"
108
+ end
109
+ end
110
+
111
+ def clone_with_scope(scope, block)
112
+ clone.tap do |query|
113
+ query.scope = scope
114
+ query.block = block
115
+ end
116
+ end
117
+
118
+ def clone_with_params(other_params)
119
+ clone.tap do |query|
120
+ query.params = params.merge(other_params)
121
+ end
122
+ end
123
+
124
+ def clone_with_sifter(block)
125
+ dup.tap do |query|
126
+ query.sifted!(block, self)
127
+ end
128
+ end
129
+
130
+ def sifted?
131
+ !!@sifted
132
+ end
133
+
134
+ def sifted_instance_for(block)
135
+ clone_with_sifter(block).sifted_instance
136
+ end
137
+
138
+ def define_attr_readers
139
+ @attrs.each do |name, value|
140
+ define_singleton_method(name){ value }
141
+ end
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,23 @@
1
+ module Parascope
2
+ class Query::ApiBlock < Struct.new(:presence_fields, :value_fields, :block, :index)
3
+ def fits?(params)
4
+ values_for(params).all?{ |value| present?(value) }
5
+ end
6
+
7
+ def values_for(params)
8
+ params.values_at(*presence_fields) + valued_values_for(params)
9
+ end
10
+
11
+ def present?(value)
12
+ value.respond_to?(:empty?) ? !value.empty? : !!value
13
+ end
14
+
15
+ private
16
+
17
+ def valued_values_for(params)
18
+ value_fields.map do |field, required_value|
19
+ params[field] == required_value && required_value
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,41 @@
1
+ module Parascope
2
+ module Query::ApiMethods
3
+ def base_scope(&block)
4
+ return @base_scope unless block_given?
5
+
6
+ @base_scope = block
7
+ end
8
+
9
+ def defaults(params = nil)
10
+ @defaults ||= {}
11
+
12
+ return @defaults if params.nil?
13
+
14
+ @defaults = @defaults.merge(params)
15
+ end
16
+
17
+ def sift_by(*presence_fields, **value_fields, &block)
18
+ sift_blocks.push Query::ApiBlock.new(presence_fields, value_fields, block)
19
+ end
20
+
21
+ def query_by(*presence_fields, index: 0, **value_fields, &block)
22
+ query_blocks.push Query::ApiBlock.new(presence_fields, value_fields, block, index)
23
+ end
24
+
25
+ def guard(&block)
26
+ guard_blocks.push block
27
+ end
28
+
29
+ def sift_blocks
30
+ @sift_blocks ||= []
31
+ end
32
+
33
+ def query_blocks
34
+ @query_blocks ||= []
35
+ end
36
+
37
+ def guard_blocks
38
+ @guard_blocks ||= []
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,3 @@
1
+ module Parascope
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,33 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'parascope/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "parascope"
8
+ spec.version = Parascope::VERSION
9
+ spec.authors = ["Artem Kuzko"]
10
+ spec.email = ["a.kuzko@gmail.com"]
11
+
12
+ spec.summary = %q{Builds a params-sifted scope}
13
+ spec.description = %q{Parascope::Query class provides a way to dynamically
14
+ apply scopes or ActiveRecord query methods based on passed params with a
15
+ declarative and convenient API}
16
+ spec.homepage = "https://github.com/akuzko/parascope"
17
+ spec.license = "MIT"
18
+
19
+ spec.required_ruby_version = '>= 2.1.0'
20
+
21
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
22
+ spec.bindir = "exe"
23
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
24
+ spec.require_paths = ["lib"]
25
+
26
+ spec.add_dependency "hashie"
27
+
28
+ spec.add_development_dependency "bundler", "~> 1.11"
29
+ spec.add_development_dependency "rake", "~> 10.0"
30
+ spec.add_development_dependency "rspec", "~> 3.0"
31
+ spec.add_development_dependency "pry"
32
+ spec.add_development_dependency "pry-nav"
33
+ end
metadata ADDED
@@ -0,0 +1,146 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: parascope
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Artem Kuzko
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-05-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: hashie
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.11'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.11'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: pry
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: pry-nav
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: |-
98
+ Parascope::Query class provides a way to dynamically
99
+ apply scopes or ActiveRecord query methods based on passed params with a
100
+ declarative and convenient API
101
+ email:
102
+ - a.kuzko@gmail.com
103
+ executables: []
104
+ extensions: []
105
+ extra_rdoc_files: []
106
+ files:
107
+ - ".gitignore"
108
+ - ".rspec"
109
+ - ".travis.yml"
110
+ - Gemfile
111
+ - LICENSE.txt
112
+ - README.md
113
+ - Rakefile
114
+ - bin/console
115
+ - bin/setup
116
+ - lib/parascope.rb
117
+ - lib/parascope/query.rb
118
+ - lib/parascope/query/api_block.rb
119
+ - lib/parascope/query/api_methods.rb
120
+ - lib/parascope/version.rb
121
+ - parascope.gemspec
122
+ homepage: https://github.com/akuzko/parascope
123
+ licenses:
124
+ - MIT
125
+ metadata: {}
126
+ post_install_message:
127
+ rdoc_options: []
128
+ require_paths:
129
+ - lib
130
+ required_ruby_version: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - ">="
133
+ - !ruby/object:Gem::Version
134
+ version: 2.1.0
135
+ required_rubygems_version: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ version: '0'
140
+ requirements: []
141
+ rubyforge_project:
142
+ rubygems_version: 2.4.8
143
+ signing_key:
144
+ specification_version: 4
145
+ summary: Builds a params-sifted scope
146
+ test_files: []