Skip to content

Commit

Permalink
Add Avram::Model#reload
Browse files Browse the repository at this point in the history
Closes #262
  • Loading branch information
paulcsmith committed Mar 16, 2020
1 parent b47b09b commit 98088a2
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 0 deletions.
26 changes: 26 additions & 0 deletions spec/model_spec.cr
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
require "./spec_helper"

include LazyLoadHelpers

class NamedSpaced::Model < BaseModel
table do
end
Expand Down Expand Up @@ -90,6 +92,30 @@ describe Avram::Model do
user.email.to_s.should eq "foo@bar.com"
end

describe "reload" do
it "can reload a model" do
user = UserBox.create &.name("Original Name")

# Update returns a brand new user. It should have the new name
newly_updated_user = User::SaveOperation.update!(user, name: "Updated Name")

newly_updated_user.name.should eq("Updated Name")
# The original user is not modified
user.name.should eq("Original Name")
# So we reload it to get the new goodies
user.reload.name.should eq("Updated Name")
end

it "can reload a model with a yielded query" do
with_lazy_load(enabled: false) do
post = PostBox.create

# If `preload_tags` doesn't work this will raise
post.reload(&.preload_tags).tags.should be_empty
end
end
end

it "sets up simple methods for equality" do
query = QueryMe::BaseQuery.new.email("foo@bar.com").age(30)

Expand Down
4 changes: 4 additions & 0 deletions src/avram/base_query_template.cr
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ class Avram::BaseQueryTemplate
{{ type }}.database
end

def query_class

end

@@table_name = :{{ table_name }}
@@schema_class = {{ type }}

Expand Down
61 changes: 61 additions & 0 deletions src/avram/model.cr
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,67 @@ abstract class Avram::Model
id.to_s
end

# Reload the model with the latest information from the database
#
# This method will return a new model instance with the
# latest data from the database. Note that this does
# **note** change the original instance, so you may need to
# assign the result to a variable or work directly with the return value.
#
# Example:
#
# ```crystal
# user = SaveUser.create!(name: "Original")
# SaveUser.update!(user, name: "Updated")
#
# # Will be "Original"
# user.name
# # Will return "Updated"
# user.reload.name # Will be "Updated"
# Will still be "Original" since the 'user' is the same model instance.
# user.name
#
# Instead re-assign the variable. Now 'name' will return "Updated" since
# 'user' references the reloaded model.
# user = user.reload
# user.name
# ```
def reload : self
base_query_class.find(id)
end

# Same as `reload` but allows passing a block to customize the query.
#
# This is almost always used to preload additional relationships.
#
# Example:
#
# ```crystal
# user = SaveUser.create(params)
#
# # We want to display the list of articles the user has commented on, so let's #
# # preload them to avoid N+1 performance issues
# user = user.reload(&.preload_comments(CommentQuery.new.preload_article))
#
# # Now we can safely get all the comment authors
# user.comments.map(&.article)
# ```
#
# Note that the yielded query is the `BaseQuery` so it will not have any
# methods defined on your customized query. This is usually fine since
# typically reload only uses preloads.
#
# If you do need to do something more custom you can manually reload:
#
# ```crystal
# user = SaveUser.create!(name: "Helen")
# UserQuery.new.some_custom_preload_method.find(user.id)
# ```
def reload : self
query = yield base_query_class.new
query.find(id)
end

macro table(table_name = nil)
{% unless table_name %}
{% table_name = run("../run_macros/infer_table_name.cr", @type.id) %}
Expand Down
5 changes: 5 additions & 0 deletions src/avram/save_operation_template.cr
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ class Avram::SaveOperationTemplate
end
end

# This makes it easy for plugins and extensions to use the base SaveOperation
def base_query_class : ::{{ type }}::BaseQuery.class
::{{ type }}::BaseQuery
end

class ::{{ type }}::SaveOperation < Avram::SaveOperation({{ type }})
{% if primary_key_type.id == UUID.id %}
before_save set_uuid
Expand Down

0 comments on commit 98088a2

Please # to comment.