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

Added reset_where method for specific columns. #325

Merged
merged 2 commits into from
Mar 16, 2020
Merged
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
17 changes: 17 additions & 0 deletions spec/query_builder_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,23 @@ describe Avram::QueryBuilder do
query.statement.should eq "SELECT * FROM users"
end

it "can reset where for a specific column" do
query = new_query
.where(Avram::Where::GreaterThan.new(:age, "18"))
.where(Avram::Where::LessThan.new(:age, "81"))
.where(Avram::Where::Equal.new(:name, "Pauline"))
query.args.should eq ["18", "81", "Pauline"]

query.reset_where("name")

query.statement.should eq "SELECT * FROM users WHERE age > $1 AND age < $2"
query.args.should eq ["18", "81"]

query.reset_where(:age)

query.args.should be_empty
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add a statement test here to match what was done on line 41?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's what I mentioned on Gitter 😂 . The result of where_sql is cached, so the test would fail. You can't call statement twice because it will always return the first result.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahhh! 👍

end

it "selects all" do
new_query.statement.should eq "SELECT * FROM users"
new_query.args.should eq [] of String
Expand Down
7 changes: 7 additions & 0 deletions spec/query_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@ describe Avram::Query do
query.args.should eq [] of String
end

it "can reset where on a specific column" do
query = UserQuery.new.name("Purcell").age(35).reset_where(&.name).query

query.statement.should eq "SELECT #{User::COLUMN_SQL} FROM users WHERE users.age = $1"
query.args.should eq ["35"] of String
end

it "can select distinct on a specific column" do
UserBox.new.name("Purcell").age(22).create
UserBox.new.name("Purcell").age(84).create
Expand Down
5 changes: 5 additions & 0 deletions src/avram/criteria.cr
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,11 @@ class Avram::Criteria(T, V)
rows.query.group_by(column)
end

# :nodoc:
def __reset_where : Avram::QueryBuilder
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way to make this private?
It feels weird to have to use __ in a lang that has real method protection.

Copy link
Contributor Author

@wout wout Mar 16, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, it is something I would do in JS. The question is, should it be private? Things like Thread for example are considered private in Crystal, while they are accessible.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It may be possible but would require reworking how Criteria and Queries work. This needs to be public so the Queryable can call it, but it shouldn't be used by the public. It can be and it'll work but it just reads strangely so adding the __ hides it a bit more.

We also do this in distinct_on so that you do MyQuery.new.distinct_on(&.name) isntead of MyQuery.new.name.distinct_on which reads super weird

Copy link
Member

@paulcsmith paulcsmith Mar 16, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm I'm not sure what you mean. If marked as private I am fairly certain it will not work since we are calling it from another class

I don't think there is a way to make this method only accessible from some classes, but not others. But maybe I'm misunderstanding

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, you're right, there isn't another way. You can't make it private. But I think the __ can be omitted. Some methods and object in Crystal are considered private, while being publicly accessible. A mention in the docs would be sufficient, I think.

rows.query.reset_where(column)
end

private def add_clause(sql_clause) : T
sql_clause = build_sql_clause(sql_clause)
rows.query.where(sql_clause)
Expand Down
11 changes: 8 additions & 3 deletions src/avram/query_builder.cr
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
class Avram::QueryBuilder
alias ColumnName = Symbol | String
getter table
getter distinct_on : String | Symbol | Nil = nil
getter distinct_on : ColumnName | Nil = nil
@limit : Int32?
@offset : Int32?
@wheres = [] of Avram::Where::SqlClause
Expand Down Expand Up @@ -122,7 +122,7 @@ class Avram::QueryBuilder
self
end

def distinct_on(column : Symbol | String)
def distinct_on(column : ColumnName)
@distinct_on = column
self
end
Expand Down Expand Up @@ -156,6 +156,11 @@ class Avram::QueryBuilder
self
end

def reset_where(column : ColumnName)
@wheres.reject! { |clause| clause.column.to_s == column.to_s }
self
end

def reset_order
@orders.clear
end
Expand Down Expand Up @@ -243,7 +248,7 @@ class Avram::QueryBuilder
.map { |column| column.split(".").last }
end

def select(selection : Array(Symbol | String))
def select(selection : Array(ColumnName))
@selections = selection
.map { |column| "#{@table}.#{column}" }
.join(", ")
Expand Down
6 changes: 6 additions & 0 deletions src/avram/queryable.cr
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@ module Avram::Queryable(T)
self
end

def reset_where(&block) : self
criteria = yield self
criteria.__reset_where
self
end

# Delete the records using the query's where clauses,
# or all the records if no wheres are added.
#
Expand Down