Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds support for per = 0 #8

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .envrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# A direnv config file: https://github.com/direnv/direnv

if [ "$(type -t direnv_load)" = 'function' ]; then
# Whatever you want only direnv to execute
PATH_add bin
fi

4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
coverage
rdoc
pkg
log/test.log
log/test.log
.rspec.status
.rake_t_cache
1 change: 1 addition & 0 deletions .tm_properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
excludeDirectories = "{coverage,log,vendor,arena,}"
34 changes: 12 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,11 @@ gives you the following options:

Note: whenever you run a query `simple-sql` takes care of sending query parameters over the wire properly. That means that you use placeholders `$1`, `$2`, etc. to use these inside your queries; the following is a correct example:

```ruby
Simple::SQL.all "SELECT * FROM users WHERE email=$1", "[email protected]"
```
Simple::SQL.all "SELECT * FROM users WHERE email=$1", "[email protected]"

Also note that it is not possible to use an array as the argument for the `IN(?)` SQL construct. Instead you want to use `ANY`, for example:

```ruby
Simple::SQL.all "SELECT * FROM users WHERE id = ANY($1)", [1,2,3]
```
Simple::SQL.all "SELECT * FROM users WHERE id = ANY($1)", [1,2,3]

### Simple::SQL.all: Fetching all results of a query

Expand All @@ -71,18 +67,14 @@ Otherwise it returns an array of arrays.

Examples:

```ruby
Simple::SQL.all("SELECT id FROM users") # returns an array of id values, but
Simple::SQL.all("SELECT id, email FROM users") # returns an array of arrays `[ <id>, <email> ]`.
```
Simple::SQL.all("SELECT id FROM users") # returns an array of id values, but
Simple::SQL.all("SELECT id, email FROM users") # returns an array of arrays `[ <id>, <email> ]`.

If a block is passed to SQL.all, each row is yielded into the block:

```ruby
Simple::SQL.all "SELECT id, email FROM users" do |id, email|
# do something
end
```
Simple::SQL.all "SELECT id, email FROM users" do |id, email|
# do something
end

### Simple::SQL.ask: getting the first result

Expand All @@ -94,10 +86,8 @@ If the SQL query returns rows with one column, this method returns the column va

Examples:

```ruby
Simple::SQL.ask "SELECT id FROM users WHERE email=$1", "foo@local" # returns a number (or `nil`) and
Simple::SQL.ask "SELECT id, email FROM users WHERE email=$?", "foo@local" # returns an array `[ <id>, <email> ]` (or `nil`)
```
Simple::SQL.ask "SELECT id FROM users WHERE email=$1", "foo@local" # returns a number (or `nil`) and
Simple::SQL.ask "SELECT id, email FROM users WHERE email=$?", "foo@local" # returns an array `[ <id>, <email> ]` (or `nil`)

### Simple::SQL.ask/Simple::SQL.all: fetching hashes

Expand All @@ -111,15 +101,15 @@ If you want the returned record to be in a structure which is not a Hash, you ca
the `into: <klass>` option. The following would return an array of up to two `OpenStruct`
objects:

sql = "SELECT id, email FROM users WHERE id = ANY($1) LIMIT 1",
sql = "SELECT id, email FROM users WHERE id = ANY($1) LIMIT 1"
Simple::SQL.all sql, [1,2,3], into: OpenStruct

This supports all target types that take a contructor which acceps Hash arguments.
This supports all target types that take a constructor which accepts Hash arguments.

It also supports a :struct argument, in which case simple-sql creates uses a Struct-class.
Struct classes are reused when possible, and are maintained by Simple::SQL.

sql = "SELECT id, email FROM users WHERE id = ANY($1) LIMIT 1",
sql = "SELECT id, email FROM users WHERE id = ANY($1) LIMIT 1"
Simple::SQL.all sql, [1,2,3], into: :struct

### Transaction support
Expand Down
5 changes: 5 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,8 @@ task default: "test:prepare_db" do
sh "USE_ACTIVE_RECORD=1 rspec"
sh "rubocop -D"
end

task :fastspec do
sh "SKIP_SIMPLE_COV=1 rspec --only-failures"
sh "rspec"
end
7 changes: 5 additions & 2 deletions lib/simple/sql/connection_adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,9 @@ def ask(sql, *args, into: nil)

def add_page_info(scope, results)
raise ArgumentError, "expect Array but get a #{results.class.name}" unless results.is_a?(Array)
raise ArgumentError, "per must be > 0" unless scope.per > 0

# optimization: add empty case (page <= 1 && results.empty?)
if scope.page <= 1 && results.empty?
if scope.page <= 1 && scope.per > 0 && results.empty?
Scope::PageInfo.attach(results, total_count: 0, per: scope.per, page: 1)
else
sql = "SELECT COUNT(*) FROM (#{scope.order_by(nil).to_sql(pagination: false)}) simple_sql_count"
Expand All @@ -80,6 +79,8 @@ def exec_logged(sql_or_scope, *args)
if sql_or_scope.is_a?(Scope)
raise ArgumentError, "You cannot call .all with a scope and additional arguments" unless args.empty?

return [] if sql_or_scope.per == 0

sql = sql_or_scope.to_sql
args = sql_or_scope.args
else
Expand All @@ -92,6 +93,8 @@ def exec_logged(sql_or_scope, *args)
end

def enumerate(result, into:, &block)
return result if result.is_a?(Array) && result.empty?

decoder = Decoder.new(self, result, into: into)

if block
Expand Down
22 changes: 12 additions & 10 deletions lib/simple/sql/scope.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,17 @@ def where(sql_fragment, arg = :__dummy__no__arg, placeholder: "?")
duplicate.send(:where!, sql_fragment, arg, placeholder: placeholder)
end

# Set pagination
def paginate(per:, page: 1)
duplicate.send(:paginate!, per: per, page: page)
end

def order_by(sql_fragment)
duplicate.send(:order_by!, sql_fragment)
end

private

def where!(sql_fragment, arg = :__dummy__no__arg, placeholder: "?")
if arg == :__dummy__no__arg
@filters << sql_fragment
Expand All @@ -63,11 +74,6 @@ def where!(sql_fragment, arg = :__dummy__no__arg, placeholder: "?")
self
end

# Set pagination
def paginate(per:, page:)
duplicate.send(:paginate!, per: per, page: page)
end

def paginate!(per:, page:)
@per = per
@page = page
Expand All @@ -81,10 +87,6 @@ def order_by!(sql_fragment)
self
end

def order_by(sql_fragment)
duplicate.order_by!(sql_fragment)
end

public

# Is this a paginated scope?
Expand Down Expand Up @@ -124,7 +126,7 @@ module PageInfo
def self.attach(results, total_count:, per:, page:)
results.extend(self)
results.instance_variable_set :@total_count, total_count
results.instance_variable_set :@total_pages, (total_count + (per - 1)) / per
results.instance_variable_set :@total_pages, per > 0 ? (total_count + (per - 1)) / per : -1
results.instance_variable_set :@current_page, page
results
end
Expand Down
2 changes: 1 addition & 1 deletion scripts/watch
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
#!/bin/bash
watchr lib,spec rspec
watchr lib,spec rake fastspec
12 changes: 12 additions & 0 deletions spec/simple/sql_scope_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,18 @@ def expects(expected_result, sql, *args)
end
end

context "with per=0" do
let(:result) { SQL.all(scope.paginate(per: 0)) }

it "returns an empty result set" do
expect(result).to eq([])
end

it "adds total_count info to the .all return value" do
expect(result.total_count).to eq(2)
end
end

context "with per=2" do
it "returns an empty array after the last page" do
result = SQL.all(scope.paginate(per: 2, page: 2))
Expand Down
2 changes: 2 additions & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
ActiveRecord::Base.logger.level = Logger::INFO

RSpec.configure do |config|
config.example_status_persistence_file_path = ".rspec.status"

config.run_all_when_everything_filtered = true
config.filter_run focus: (ENV["CI"] != "true")
config.expect_with(:rspec) { |c| c.syntax = :expect }
Expand Down
4 changes: 4 additions & 0 deletions spec/support/004_simplecov.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
unless ENV['SKIP_SIMPLE_COV']

require "simplecov"

# make sure multiple runs result in multiple result set. SimpleCov will take
Expand All @@ -12,3 +14,5 @@

minimum_coverage 90
end

end