-
Notifications
You must be signed in to change notification settings - Fork 148
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
GH-44 implement driver.Valuer and sql.Scanner for Amount and Currency #99
Conversation
Money value was simplified, please adjust this code for the new type format. See that "val" was removed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good work
db.go
Outdated
// let's support string and int64 | ||
switch src.(type) { | ||
case string: | ||
if i, err := strconv.ParseInt(src.(string), 10, 64); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My doubt here is the string representation of an amount should have the decimal separator or not.
Introducing the decimal separator in the string representation of an amount has the benefit of being explicit on it, but has the drawback of "," is a valid decimal separator for some currencies.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is easy to change.
we might use a pipe (|
) or an at (@
) or a colon (:
) or semi-colon (;
)
or even could implement this value separator as a package level variable (for example money.DBValueSeparator
) with a default value and allow the client to override it as needed.
do you have a preference?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
refactored this out and made it configurable, updated the default to a pipe (|
)
db.go
Outdated
|
||
// Value implements driver.Valuer to serialize an Amount into sql/driver compatible type | ||
func (a Amount) Value() (driver.Value, error) { | ||
return a.val, nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"val" was removed from Amount
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: in the last change, Amount was converted to an alias of "int64".
type Amount = int64
For this method to work you will need to change it to a new type:
type Amount int64
(Removing the "=" sign)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this will simplify the db.go
file as the driver.Valuer
and sql.Scanner
interfaces are already implemented for the base int64
type.
I am planning to rebase my branch on top of the recent changes and handle the Amount updates that way.
than you for the feedback; I will look to get this PR updated shortly. |
4998531
to
0a9aa04
Compare
this PR has been rebased on top of as well the database value separator has been factored out to to a package level variable to allow clients to customize it and the default value separator changed from |
This allows any Golang ORM which supports the sql.Scanner to serialize (via sql.Driver) and deserialize (via sql.Scanner) a money.Currency instance. money.Amount is now a type alias to int64 which is already supported by sql.Scanner as one of the core built-in data types
Money's Value() function enables compatible sql drivers to serialize a money.Money instance to a single comma-delimited string value of "amount,currency_code" Money's Scan() function assumes that it receives a single column where the src value is a comma- delimited string in the format "amount,currency_code" While the storage format is up to the client when the amount and currency are stored separately a compatible scanner value can be created in SQLite like this: SELECT amount || "," || currency as 'amount' It is left to the client to decide to use Money's Valuer implementation with a db annotation on a property of type Money or else to store the Amount and Currency values as two separate columns and return them as a single joined string field.
strings.Split(src,,) will return a slice with length 2 even if one of the strings is empty
clients can set money.DBMoneyValueSeparator to determine which separator (e.g. "," "|" ";" ":" "AS" etc) to use when creating a single driver.Value object to represent a money.Money instance as a single string database field. this allows the money package to support string values such as 10@USD 20;CAD 30|IRD 40 in GBP etc
Implements the
driver.Valuer
andsql.Scanner
interfaces formoney.Currency
.Implements the
sql.Scanner
interface formoney.Money
such that it expects to read a single string value of the format "amount|currency_code"This allows the client to save a
money.Money
object in a single string field like this "amount|currency_code" or in two discrete where "amount" and "currency" are stored separately and recomposed for scanning like this:If the pipe (
|
) delimeter causes problems for your use case you can customize it by settingmoney.DBMoneyValueSeparator
to a different value; this package-levelvar
is used when converting amoney.Money
object to a string Value and also when scanning a string Value back into amoney.Money
instance.I have tested this in my own project where the amount is stored as an integer field and the currency code is stored as a text field and they two are joined together in a hand-written SELECT query before passing it to
money.Scan()
In my project I am using
sqlx
so I am fairly certain that this implementation will support any ORM or db wrapper which makes use of thesql.Scanner
interface.