g5 / storext Goto Github PK
View Code? Open in Web Editor NEWAdd type-casting and other features on top of ActiveRecord::Store.store_accessor
License: MIT License
Add type-casting and other features on top of ActiveRecord::Store.store_accessor
License: MIT License
Noticing that Boolean values when updated from a Rails form are being stored as "1" or "0" instead of true/false.
More Info:
example_setting Boolean, default: true
If I set example_setting
from a console. object.example_setting = true
. It is stored correctly.
{ "example_setting": true }
But if I submit it from a Rails form (which uses 1 or 0 for true/false).
{ "example_setting": "1" }
This still works as expected & doesn't cause any problems. But I think it would be best if 1's and 0's are translated to true/false for consistency.
Note: I'm using JSONB for my storeaccessor.
When extracting data from JSONb where the values are the empty string, e.g. "", the gem will return them as nil into Rails.
Using <<
doesn't work. Using =
or +=
does. I just found this strange. This might not be specific to this gem but I thought I would bring it up anyway. Is this expected behavior?
Sorry for the long issue, but it's a bit hard to explain it without code examples. I promise this is not that long.
Just asked a question on SO because I thought it's a general problem and after few hours and a bit of help I realised there might be something wrong when you write scopes and use Storext.
On a *dummy app, there is a jsonb column called financing_offer
on a car model, no storext and a scope.
* Courtesy to juanitofatas who tried to help me and created the dummy.
class Car < ApplicationRecord
# include Storext.model
scope :financing_offer_eq, ->(field, value) do
where(
"cars.financing_offer->>:field = :value",
field: field,
value: value
)
end
# store :financing_offer, coder: JSON
#
# store_attributes :financing_offer do
# down_payment String
# end
end
I have created two records like this:
Car.create(financing_offer: {"down_payment" => "1000"}.to_json)
(0.2ms) BEGIN
SQL (0.6ms) INSERT INTO "cars" ("financing_offer", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["financing_offer", "\"{\\\"down_payment\\\":\\\"1000\\\"}\""], ["created_at", 2016-08-31 19:44:23 UTC], ["updated_at", 2016-08-31 19:44:23 UTC]]
(1.3ms) COMMIT
=> #<Car id: 1, financing_offer: "{\"down_payment\":\"1000\"}", created_at: "2016-08-31 19:44:23", updated_at: "2016-08-31 19:44:23">
Car.create(financing_offer: {"down_payment" => "1000"})
(0.5ms) BEGIN
SQL (0.4ms) INSERT INTO "cars" ("financing_offer", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["financing_offer", "{\"down_payment\":\"1000\"}"], ["created_at", 2016-08-31 19:52:02 UTC], ["updated_at", 2016-08-31 19:52:02 UTC]]
(1.2ms) COMMIT
=> #<Car id: 2, financing_offer: {"down_payment"=>"1000"}, created_at: "2016-08-31 19:52:02", updated_at: "2016-08-31 19:52:02">
Both look the same with a slight difference. In the first one financing_offer is saved as json and in the second one as ruby hash.
Running the scope without storext, the second record is returned because the first one has only serialized json.
Car.financing_offer_eq(:down_payment, '1000')
Car Load (0.8ms) SELECT "cars".* FROM "cars" WHERE (cars.financing_offer->>'down_payment' = '1000')
=> #<ActiveRecord::Relation [#<Car id: 2, financing_offer: {"down_payment"=>"1000"}, created_at: "2016-08-31 19:52:02", updated_at: "2016-08-31 19:52:02">]>
After enabling storext, there is a problem with record 2: TypeError: no implicit conversion of Hash into String
and it's fine.. Storext expects that financing_offer is JSON when in fact it isn't. I removed record 2 so I can go on.
But the first record looks like this now (perfectly fine and this is why I wanna use this gem):
Car.first
Car Load (0.4ms) SELECT "cars".* FROM "cars" ORDER BY "cars"."id" ASC LIMIT $1 [["LIMIT", 1]]
=> #<Car id: 1, financing_offer: {"down_payment"=>"1000"}, created_at: "2016-08-31 19:44:23", updated_at: "2016-08-31 19:44:23">
Although because it's still serialized in the db and after the query AR+Storext displays it as ruby hash the scope can't capture it:
Car.financing_offer_eq(:down_payment, '1000')
Car Load (0.6ms) SELECT "cars".* FROM "cars" WHERE (cars.financing_offer->>'down_payment' = '1000')
=> #<ActiveRecord::Relation []>
If you create new records now, even though you send Ruby hash on creation, it will be saved as serialized JSON in the DB, hence the scope will not work.
Car.create(financing_offer: {"down_payment" => "1000"})
(0.1ms) BEGIN
SQL (6.8ms) INSERT INTO "cars" ("financing_offer", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["financing_offer", "\"{\\\"down_payment\\\":\\\"1000\\\"}\""], ["created_at", 2016-08-31 20:20:31 UTC], ["updated_at", 2016-08-31 20:20:31 UTC]]
(7.2ms) COMMIT
=> #<Car id: 3, financing_offer: {"down_payment"=>"1000"}, created_at: "2016-08-31 20:20:31", updated_at: "2016-08-31 20:20:31">
irb(main):022:0> Car.financing_offer_eq(:down_payment, '1000')
Car Load (0.3ms) SELECT "cars".* FROM "cars" WHERE (cars.financing_offer->>'down_payment' = '1000')
=> #<ActiveRecord::Relation []>
Hi, when I use gem audited, it does not get audits from columns changes, do you have any advice?
https://github.com/collectiveidea/audited
Hello,
is there any way to define conditional attributes? I need to do something like this:
class MyModel < ApplicationRecord
include Storext.model
enum model_type: { basic: 1, better: 2 }
store_attributes :settings do
setting_1 Boolean, default: false, if: proc { |m| m.basic? }
setting_2 Boolean, default: false, if: proc { |m| m.better? }
end
end
How can I achieve this? Thank you.
Hello, love this gem :)
What do you think of auto defining predicate methods for boolean values? (to match what Rails does for booleans).
some_setting Boolean, default: true
object.some_setting? # => true
If you like this idea, I'd be happy to implement.
When default is a symbol or a proc that calls a method on the instance, things do not work as expected. This is probably the case because of the proxy class architecture.
Hi,
In the dependencies only active record < 6.2 is allowed. Is it possible to allow a higher version to be used with rails 7?
Thanks!
Hey,
first of all, thanks for writing this great gem!
Are there any plans to replace the 'Virtus' dependency with the much better maintained dry-types coercion engine? (Same creator)
It's even the default coercion backend for the Trailblazer architecture.
Btw, the creator of Virtus explains why he started developing the dry-types gem: Virtus to be abandoned by its creator?
Would love to hear your thoughts about it.
With storext the methods from ActiveRecord::Dirty like attribute_was, attribute_changed? do not work properly on attributes which are initialized with store_attributes:
book.title
=> "Great Voyage"
book.title = "New Title"
=> "New Title"
book.title_was
=> "New Title"
book.title_changed?
=> false
Especially for validations with if conditions this would be very important.
Any ideas where in the code this could be added/fixed?
I have successfully implemented Storext in my application and it is working well.
However now I want to encrypt the data in the :data
column to which Storext writes. I tried to use the serialize
function on the column using information in this post http://iempire.ru/2015/12/18/simple-rails-encryption/. but the data is not encrypted.
I am assuming that the serialize
is being overloaded somewhere by Storext to ensure that the data is stored as a JSON object.
Is there a way that I am missing that would allow me to incorporate encryption and Storext?
Marshal.dump
fails on any object if default value is not defined for Jsonb attributes
rails new storext-demo --api
pg
gem, add storext
gemrails g model book title:string data:jsonb
class Book < ApplicationRecord
include Storext.model
store_attributes :data do
sync Boolean, default: true
sync_at DateTime, default: 0
end
end
rails g model post title:string data:jsonb
class Post < ApplicationRecord
include Storext.model
store_attributes :data do
sync Boolean
sync_at DateTime
end
end
Marshal.dump
works on model with default valueBook.create title:'Lorem Ipsum'
Marshal.dump Book.first
==> OKMarshal.dump
fails on model without default valuePost.create title:'Lorem Ipsum'
Marshal.dump Post.first
==> KO TypeError: can't dump anonymous class #<Class:0x007fefcdab1340>
Hi there.
I just wanted to let you know that the 3.0.0 gem has some file permissions issues. All the files inside of the 3.0.0 gem have 600
permissions. This means that if you have an environment that installs the gems as one user and then runs your application as another the application won't be able to load the gem. This issue can also be tough to see coming too because people normally only develop locally with one user so it wouldn't be encountered until a deployment that uses two users.
We just got hit by this during a deployment. This is not an issue with the 2.2.2 gem.
Love the gem, very handy, thank you.
Has anyone figured out how to use Storext with Ransack search features?
Hello,
Apologies this is not a "bug", but github doesn't really provide another way to ask a question.
I'm trying to use this gem to handle preference settings for my project, and I'm trying to get each "setting" to correspond to a defined enum
. I have defined my settings as an integer like: foo_setting Integer, default: 0
, and I'd like the integer to go through a pre-defined enum and return a param, instead of a single integer.
Is this possible with this gem? I'm transitioning from defining each setting as a separate column in the database, which then linked to an enum list.
Thanks for your time.
You get the error when you have code like
store_attributes :body do
hash String
end
You can work around it by store_attribute :body, :hash, String
.
hash
in a block, and if so, how?store_attribute
syntax instead?)Hello again,
I noticed I get errors if I move store_attributes in a concern and use the Boolean type.
module CarLeasing
extend ActiveSupport::Concern
included do
store_attributes :leasing_offer do
leasing_has_rest_debt Boolean
end
end
end
pry(main)> Car.storext_definitions
NameError: uninitialized constant CarLeasing::Boolean
However inside the model which includes this concern everything works:
{:leasing_has_rest_debt=>{:column=>:leasing_offer, :type=>Axiom::Types::Boolean (BasicObject), :opts=>{}}}
Is it not suitable to use concerns together with storext?
Thanks!
Will there be a fix for Rails 6.1 support anytime soon or will this only work with < Rails 6.1 for the forseeable future? Getting this bug when trying to bundle install
a new Rails 6.1 project:
Bundler could not find compatible versions for gem "rails":
In snapshot (Gemfile.lock):
rails (= 6.1.0)
In Gemfile:
rails (~> 6.1.0) x64-mingw32
storext x64-mingw32 was resolved to 2.0.1, which depends on
rails (~> 4.0) x64-mingw32
Hey,
Had this idea, not sure if currently possible.
Would be nice if we could set a default to be based on another existing value.
Use Case:
Such as:
store_attributes :email_settings do
subscribed_to_email Boolean, default: true
subscribe_to_comment_notification_email Boolean, default: subscribed_to_email
end
Is it possible today?
If not - any interest in having this ability in the gem? (I can work on it)
I've found this gem to be really useful, but I often want to store nested data. I have a monkey patch for storing ActiveModel-compatible objects that I've been passing around among various projects. Would you accept a pull request to integrate this directly into the gem?
Hi,
Would this project be interested in a PR to start using Github Actions instead of Travis? I'll be happy to take a go at it if this is interesting.
class Foo < ActiveRecord::Base
include Storext.model
store_attributes :settings do
attribute_name String
end
end
Foo.where(some_attr: some_value).select(:some_attr).first
# => ActiveModel::MissingAttributeError: missing attribute: settings
Hello guys,
It might be a weird need but is there a plan/are you willing to accept a PR that introduce a namespace/scope/prefix concept around the stored attributes when there is a need for identical name?
I'm thinking at:
class Car
include Storext.model
store_attributes :financing_offer, namespace: :financing do
monthly_payment Float
....
end
store_attributes :leasing_offer, namespace: :leasing do
monthly_payment Float
....
end
end
Which will define monthly_payment
accessor with a prefix
so that there is no clash between attributes with same name.
car.leasing_monthly_payment => 0.11
car.financing_monthly_payment => 0.22
car.monthly_payment => NoMethodError: undefined method `monthly_payment'
Of course you can directly choose a different name but I'm wondering if it can be something beneficial at some point :)
I would like to do something like this:
But I get this error on group.save!
:
PG::UndefinedColumn: ERROR: column groups.name does not exist
LINE 1: SELECT 1 AS one FROM "groups" WHERE "groups"."name" = $1 LIM...
How can I make it work?
Thank you for the help. I'm still trying to figure out how to work with JSONB columns...
Currently use storext on jsonb columns on my models and just realized that when updating the hash column with certain values for keys I want to update, it destroys the rest of the hash (because you're essentially setting that column to equal the new hash you're passing in)
If it would be useful to the rest of the community here I'd be willing to open a pull request that enables the ability to update specific values for some keys while preserving the values of keys not passed in when updating the column storext is on.
I can also send an example if what I'm trying to achieve isn't clear.
Rails 6.0.0
was just released (see: https://github.com/rails/rails/releases/tag/v6.0.0).
Currently, storext requires Rails 4.x or Rails 5.x.
It seems that now is the time to support Rails 6.0.0.
Is there a sense of how much work is required?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.