restforce-query 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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 5f3b0b1326cc3430f270d4627d953a16504ab9785a3cb0d45a5f5374c1e9cdb6
4
+ data.tar.gz: 6fb031b51a294bae4ee48d6a962de7486e81d716b53c5fbe6a75f08c3d9d287d
5
+ SHA512:
6
+ metadata.gz: 0efe646dcf43283e6ddfd51ff78774d497e89d1eab8ec2111bc8216c0f6dfe113f36de72299f3aa5c0481350fcd5df207a8d1602adcae2670a4dd1e473339b01
7
+ data.tar.gz: 129e01abc9caa4206f1b54b6c39df5da3a6a8d009265db0eeb5630a40b733d0362bc0f9f59b732bd0e63deacd58935b923ca6fa515dbedfd4d85d06fd87a7813
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.5.1
5
+ before_install: gem install bundler -v 1.16.1
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in restforce-query.gemspec
6
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,48 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ restforce-query (0.1.0)
5
+ restforce
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ diff-lcs (1.3)
11
+ faraday (0.15.1)
12
+ multipart-post (>= 1.2, < 3)
13
+ faraday_middleware (0.12.2)
14
+ faraday (>= 0.7.4, < 1.0)
15
+ hashie (3.5.7)
16
+ json (2.1.0)
17
+ multipart-post (2.0.0)
18
+ rake (10.5.0)
19
+ restforce (2.5.3)
20
+ faraday (>= 0.9.0, <= 1.0)
21
+ faraday_middleware (>= 0.8.8, <= 1.0)
22
+ hashie (>= 1.2.0, < 4.0)
23
+ json (>= 1.7.5)
24
+ rspec (3.7.0)
25
+ rspec-core (~> 3.7.0)
26
+ rspec-expectations (~> 3.7.0)
27
+ rspec-mocks (~> 3.7.0)
28
+ rspec-core (3.7.1)
29
+ rspec-support (~> 3.7.0)
30
+ rspec-expectations (3.7.0)
31
+ diff-lcs (>= 1.2.0, < 2.0)
32
+ rspec-support (~> 3.7.0)
33
+ rspec-mocks (3.7.0)
34
+ diff-lcs (>= 1.2.0, < 2.0)
35
+ rspec-support (~> 3.7.0)
36
+ rspec-support (3.7.1)
37
+
38
+ PLATFORMS
39
+ ruby
40
+
41
+ DEPENDENCIES
42
+ bundler (~> 1.16)
43
+ rake (~> 10.0)
44
+ restforce-query!
45
+ rspec (~> 3.0)
46
+
47
+ BUNDLED WITH
48
+ 1.16.1
data/README.md ADDED
@@ -0,0 +1,39 @@
1
+ # Restforce::Query
2
+
3
+ This gem provides a DSL for making restforce queries and avoid writing SOQL by hand.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'restforce-query'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install restforce-query
20
+
21
+ ## Usage
22
+
23
+ Example:
24
+ ```ruby
25
+ Restforce::Query.select('LeadSource', 'COUNT(id)')
26
+ .from('Opportunity')
27
+ .where(StageName: 'Closed Won')
28
+ .group_by('LeadSource')
29
+ ```
30
+
31
+ ## Development
32
+
33
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
34
+
35
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
36
+
37
+ ## Contributing
38
+
39
+ Bug reports and pull requests are welcome on GitHub at https://github.com/santi698/restforce-query.
data/Rakefile ADDED
@@ -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
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "restforce/query"
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
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -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,93 @@
1
+ require_relative 'where_renderer'
2
+
3
+ module Restforce
4
+ class Query
5
+ # Private class, used by Restforce::Query
6
+ class Builder
7
+ MAX_QUERY_SIZE = 20_000
8
+ QueryTooLargeError = StandardError.new("Maximum query size of #{MAX_QUERY_SIZE} exceeded")
9
+ def initialize
10
+ @fields = []
11
+ @tables = []
12
+ @custom_conditions = []
13
+ @conditions = {}
14
+ @groupings = []
15
+ @limit = nil
16
+ @distinct = false
17
+ end
18
+
19
+ def select(*fields)
20
+ @fields += (fields.flatten - @fields)
21
+ self
22
+ end
23
+
24
+ def select_distinct(*fields)
25
+ @fields += (fields.flatten - @fields)
26
+ @groupings += (fields.flatten - @groupings)
27
+ self
28
+ end
29
+
30
+ def distinct
31
+ @distinct = true
32
+ self
33
+ end
34
+
35
+ def from(*tables)
36
+ @tables = tables.flatten
37
+ self
38
+ end
39
+
40
+ def where(custom_condition = nil, **conditions)
41
+ @custom_conditions << "(#{custom_condition})" unless custom_condition.nil?
42
+ @conditions = @conditions.merge(conditions)
43
+ self
44
+ end
45
+
46
+ def group_by(*groupings)
47
+ @groupings += (groupings.flatten - @groupings)
48
+ self
49
+ end
50
+
51
+ def limit(lim)
52
+ @limit = lim
53
+ self
54
+ end
55
+
56
+ def render
57
+ raise 'There must be at least one field in the SELECT clause' if @fields.empty?
58
+ raise 'There must be at least one table in the FROM clause' if @tables.empty?
59
+ result = "#{render_select}#{render_from}#{render_where}#{render_group_by}#{render_limit}"
60
+ raise QueryTooLargeError if result.size > MAX_QUERY_SIZE
61
+ result.strip
62
+ end
63
+
64
+ alias to_s render
65
+
66
+ private
67
+
68
+ def render_select
69
+ "SELECT #{@fields.join(', ')}"
70
+ end
71
+
72
+ def render_from
73
+ " FROM #{@tables.join(', ')}"
74
+ end
75
+
76
+ def render_where
77
+ WhereRenderer.render(@custom_conditions, @conditions)
78
+ end
79
+
80
+ def render_group_by
81
+ # This line adds all fields in the select which were not explicitly grouped
82
+ @groupings += (@fields - @groupings) if @distinct
83
+ return '' if @groupings.empty?
84
+ " GROUP BY #{@groupings.join(', ')}"
85
+ end
86
+
87
+ def render_limit
88
+ return '' if @limit.nil?
89
+ " LIMIT #{@limit}"
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,5 @@
1
+ module Restforce
2
+ class Query
3
+ VERSION = '0.1.0'.freeze
4
+ end
5
+ end
@@ -0,0 +1,54 @@
1
+ module Restforce
2
+ class Query
3
+ # Private class, used by Restforce::Query
4
+ class WhereRenderer
5
+ MAX_WHERE_SIZE = 12_000
6
+ WhereTooLargeError = StandardError.new("Maximum WHERE size of #{MAX_WHERE_SIZE} exceeded")
7
+ def initialize(custom_conditions, conditions)
8
+ @custom_conditions = custom_conditions || []
9
+ @conditions = conditions || []
10
+ end
11
+
12
+ def self.render(custom_conditions, conditions)
13
+ new(custom_conditions, conditions).render
14
+ end
15
+
16
+ def render
17
+ conditions_text = render_custom + render_connector + render_conditions
18
+ return '' if conditions_text.strip.size.zero?
19
+ result = " WHERE #{conditions_text}"
20
+ raise WhereTooLargeError if result.size > MAX_WHERE_SIZE
21
+ result
22
+ end
23
+
24
+ private
25
+
26
+ def render_conditions
27
+ return '' if @conditions.count.zero?
28
+ @conditions.map do |k, v|
29
+ next "#{k} IN #{render_list(v)}" if v.is_a? Enumerable
30
+ next "#{k} = NULL" if v.nil?
31
+ "#{k} = #{render_item(v)}"
32
+ end.join(' AND ')
33
+ end
34
+
35
+ def render_connector
36
+ (@conditions.count > 0) && (@custom_conditions.count > 0)? ' AND ' : ''
37
+ end
38
+
39
+ def render_custom
40
+ return '' if @custom_conditions.count.zero?
41
+ @custom_conditions.join(' AND ')
42
+ end
43
+
44
+ def render_list(*items)
45
+ "(#{items.flatten.map { |i| render_item(i) }.join(',')})"
46
+ end
47
+
48
+ def render_item(item)
49
+ return "'#{item}'" if item.is_a? String
50
+ item
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,47 @@
1
+ require 'restforce'
2
+ require 'restforce/query/version'
3
+ require 'restforce/query/builder'
4
+ require 'restforce/query/where_renderer'
5
+
6
+ module Restforce
7
+ # This is the main entry point for starting a query
8
+ # Chain calls to: select, where, distinct, from, group_by, limit or select_distinct to use it
9
+ class Query
10
+ include Enumerable
11
+ def initialize(query_builder = Builder.new)
12
+ @query_builder = query_builder
13
+ @salesforce = Restforce.new
14
+ end
15
+
16
+ %i[select select_distinct distinct where from group_by limit].each do |method_name|
17
+ # Defines instance methods and returns self to enable chaining
18
+ define_method(method_name) do |*params|
19
+ @query_builder.send(method_name, *params)
20
+ # Flush cache results each time the query is modified
21
+ @results = nil
22
+ self
23
+ end
24
+ # Defines class methods for easy access. i.e.: SalesforceQuery.where(...)
25
+ define_singleton_method(method_name) do |*params|
26
+ new.send(method_name, *params)
27
+ end
28
+ end
29
+
30
+ def execute
31
+ return @results if @results.present?
32
+ query = @query_builder.to_s
33
+ Rails.logger.debug(query)
34
+ @results = @salesforce.query(query)
35
+ end
36
+
37
+ def each
38
+ execute
39
+ end
40
+
41
+ def find
42
+ @query_builder.limit(1)
43
+ execute
44
+ @results.first
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,26 @@
1
+
2
+ lib = File.expand_path('lib', __dir__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'restforce/query/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'restforce-query'
8
+ spec.version = Restforce::Query::VERSION
9
+ spec.authors = ['Santiago Ocamica']
10
+ spec.email = ['santi6982@gmail.com']
11
+
12
+ spec.summary = 'A Query DSL for Restforce'
13
+ spec.homepage = 'https://github.com/santi698/restforce-query'
14
+
15
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
16
+ f.match(%r{^(test|spec|features)/})
17
+ end
18
+ spec.bindir = 'exe'
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ['lib']
21
+
22
+ spec.add_development_dependency 'bundler', '~> 1.16'
23
+ spec.add_development_dependency 'rake', '~> 10.0'
24
+ spec.add_development_dependency 'rspec', '~> 3.0'
25
+ spec.add_runtime_dependency 'restforce'
26
+ end
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: restforce-query
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Santiago Ocamica
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-05-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.16'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.16'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: restforce
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description:
70
+ email:
71
+ - santi6982@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - ".rspec"
78
+ - ".travis.yml"
79
+ - Gemfile
80
+ - Gemfile.lock
81
+ - README.md
82
+ - Rakefile
83
+ - bin/console
84
+ - bin/setup
85
+ - lib/restforce/query.rb
86
+ - lib/restforce/query/builder.rb
87
+ - lib/restforce/query/version.rb
88
+ - lib/restforce/query/where_renderer.rb
89
+ - restforce-query.gemspec
90
+ homepage: https://github.com/santi698/restforce-query
91
+ licenses: []
92
+ metadata: {}
93
+ post_install_message:
94
+ rdoc_options: []
95
+ require_paths:
96
+ - lib
97
+ required_ruby_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ required_rubygems_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ requirements: []
108
+ rubyforge_project:
109
+ rubygems_version: 2.7.6
110
+ signing_key:
111
+ specification_version: 4
112
+ summary: A Query DSL for Restforce
113
+ test_files: []