madeintandem / hstore_accessor Goto Github PK
View Code? Open in Web Editor NEWAdds typed hstore-backed field support to ActiveRecord models.
License: MIT License
Adds typed hstore-backed field support to ActiveRecord models.
License: MIT License
Let's say I have an hstore that's all integers, or all booleans.
Could we do something like hstore_accessor :options, all: :boolean
?
Maybe even something dynamic? If the attribute is 'true' or 'false', catch the boolean. If calling .to_i
or .to_f/d
evaluates to something greater than 0, catch the int or float. That might be a bit much... just thinking out loud.
What about adding inquiry methods (e.g. Car.suv?) for attributes?
I think it'd be pretty cool.
After searching for a little while I discovered that Rails' implementation for them is not that simple. For that reason, we should use query_attribute() if we want to do that in hstore_accessor as well.
What do you guys thing about it?
When a value is not set it shouldn't be casted as that would lead to unexpected behaviour.
For instance:
And so on...
Ex. "1,2,3" should split to ["1", "2", "3"]
It would be nice to extend the accessors to correctly track dirty status in the same way as native fields and add _changed?
_was?
and _change
methods
Hi
I tried to use gem with rails 4.2 but it doesn't work because it have different
typecasting inferface
methods used here doesn't exist anymore.
Here is short description what exactly changed in latest release
http://metaskills.net/2015/01/06/activerecord-42s-type-casting/
Do you want to support that feature ? I could try to prepare pr but I need your opinion first
I saw you already added this feature. Could you send new version to rubygems because latest one doesn't contain code for rails 4.2
Is there a plan to update the dirty tracking to rails 5.1 style?
Ex.
saved_change_to_{attribute}?
will_save_change_to_{attribute}?
In case someone comes looking for it, here is the equivalent of getting the list of attributes (keys) for a given hstore. It's briefly mentioned in the README (bottom paragraph), but wasn't initially clear for me.
# ActiveRecord::Store
class Event < Content
store_accessor :mydata, :name, :cost, :sponsor
end
Event.stored_attributes[:mydata]
# => [:name, :cost, :sponsor]
# Hstore Accessor gem
class Event < Content
hstore_accessor :mydata, name: :string, cost: :string, sponsor: :string
end
Event.hstore_metadata_for_mydata.keys
# => [:name, :cost, :sponsor]
For STI cases, I added a method on the parent class to get the keys and fail safely if a child class does not have any hstore attributes defined.
class Content < ActiveRecord::Base
def self.mydata_attribute_names
self.hstore_metadata_for_mydata.keys rescue []
end
delegate :mydata_attribute_names, to: :class
end
This gem looks great. Thanks for creating it and making it open-source!
I'm curious about the choice of YAML for serialization. If I'm not mistaken, it's probably not as fast as JSON or perhaps other serialization formats?
Any interest in adding this to the gem itself? I find myself using using the following function in my models that I pass into simple_form builders to have the fields show up as the correct type, and not just default to text after receiving nil.
def column_for_attribute(attr)
atr = hstore_metadata_for_properties[attr]
if attr
OpenStruct.new(type: atr)
else
super(attr)
end
end
The gem specifies that it works with ActiveRecord 3.2 and higher. However, the above method only seems to be available from ActiveRecord 4.0. So attributes of type date don't work. I hope I am missing something, otherwise it may be a good idea to drop 3.2 support or use the string_to_date method when ActiveRecord 3.2 is in use.
When you have multiple hstore columns for one model it is not possible to use hstore_accessor, because no prefix/scope can be set or did I miss something?
I use the following config in my model.
class MyModel < AR::Base
hstore_accessor :hstore_1, my_key: :integer
hstore_accessor :hstore_2, my_key: :integer
end
The last hstore_accessor call overwrites the first one, which seems reasonable. It would be nice if one can set a prefix which results in different query methods instead of MyModel.my_key
. What about this one?
MyModel.hstore_1_my_key
MyModel.hstore_2_my_key
https://github.com/rails/rails/blob/master/activerecord/lib/active_record/connection_adapters/column.rb value_to_boolean and others are deprecated now, so hstore_accessor isn't working with new rails 4.2 beta.
The store_accessor will allow to have a prefix to your data, to be able to differentiate in case you have similar names, in my case I have origin
& destination
which both have latitude
& longitude
, currently they're overlapping each others, is there a way to have a prefix?
it should be possible to add default value, like
hstore_accessor :settings, share_via_facebook: :boolean, default: false
Hi, I'm trying to use hstore_accessor with Rails 4 and I get this error NoMethodError: undefined method
options' for #MyModel:0x007f9a452ba458`. Any idea where it may come from ?
Thanks
My problem is I have an array field has an hstore accessor and when I present it on a form the value include the empty string(rails default behaviour), for example: ["", "1", "2"]. And for obvious reasons I would like to save it without the "". So I have tried:
class Standard < ActiveRecord::Base
# Extensions
hstore_accessor :properties, specific_content: :array
def specific_content=(value)
value = value.reject!(&:blank?)
super(value)
end
end
First I get an undefined method `reject!' for nil:NilClass, so I was getting the impression this was called with the wrong value, but I could see when I printed the values that it was first called with ["", "1", "2"] and the with nil. So I have tried and add a value = value.reject!(&:blank?) if value and this gives a stack level too deep and I can see that the value is printed infinitely.
So my conclusion is I cant extend the setter with something I need to do before it's called, since it seems to override what hstore_accessor is doing!
I can see here: https://github.com/devmynd/hstore_accessor/blob/master/lib/hstore_accessor/macro.rb#L25 that's where the definition happens. Do you think it's possible to change it to allow me to do something like above? Or should I be doing this in another way?
In relation to: #18
Let's say I have a Car
model with an hstore field called caracteristics
that contains an integer called maximum_speed
like this :
class Car < ActiveRecord::Base
hstore_accessor :caracteristics, maximum_speed: :integer
validates :maximum_speed, numericality: true, allow_nil: true
end
The problem is that it is impossible to set the value of maximum_speed
to anything other than an integer to test validation :
[1] car = Car.new
[2] car.maximum_speed = "test"
=> "test"
[3] car.maximum_speed
=> 0
The value is automatically set to 0 if it is not a number. We then cannot test validations because these are run after the integer cast.
The typecast behavior is standard for an ActiveRecord model but how can we use model validations ?
Here's what I'd expect for a normal ActiveRecord field:
class Car < ActiveRecord::Base
validates :maximum_speed, numericality: true, allow_nil: true
end
> c = Car.new
> c.maximum_speed = 'test'
=> "test"
> c.maximum_speed
=> "test"
> c.valid?
=> false
> c.errors
=> #<... @messages={:duration=>["is not a number"]}>
Regards
Was working on creating a scope that joins two tables and ran into an issue where the column reference was ambiguous. It would be helpful if the scope had a table reference on it. Got around the issue by qualifying the column.
When you run Klas.xx_contains([array])
, and array contains more than two strings, the operation fails.
To fix, apply this change to macro.rb:80
old line:
where("string_to_array(#{query_field}, '#{Serialization::SEPARATOR}') @> string_to_array(?, '#{Serialization::SEPARATOR}')", Array[value].flatten
new line:
where("string_to_array(#{query_field}, '#{Serialization::SEPARATOR}') @> string_to_array(?, '#{Serialization::SEPARATOR}')", Array[value].flatten.join(Serialization::SEPARATOR))
If I have...
class Model < ActiveRecord::Base
hstore_accessor :data, some_array: :array
end
and I call...
model = Model.new
model.some_array
it will raise an exception...
NoMethodError: undefined method `split' for nil:NilClass
Typecasting of values as they are set was added with #9 . This is causing my models to behave in a manner different than a normal ActiveRecord model attributes. Particularly, when passing in a string to an hstore attribute with the :integer type, a non-numerical string gets cast to 0. I would expect the model to accept the value and then my numericality validator to report the error when calling valid?
Example:
class MyModel < ActiveRecord::Base
hstore_accesor :data, :number => :integer
validates :number, numericality: { only_integer: true }
end
m = MyModel.new :number => 'lkj'
m.valid? #returns true
m.number #returns 0
In my opinion, this is causing the model to make an assumption about the input it should not be. This is particularly true when passing data in from a params hash from a form.
m.update_attributes params[:my_model]
The model should not be assuming that the user who submitted the form meant to put zero instead of lkj. This forces me as a developer to put this validation logic in the controller before being passed to the model but that is not the best place for such logic.
I was able to recreate this bug in 0.9.0+ . This bug does not exist in 0.6.1. FYI I'm using rails 4.0.4
hstore_accessor :properties,
first_name: :string,
website_url: :string
user = User.find(1)
user.first_name
=> "Harry"
user.first_name_changed?
=> false # correct
user.website_url = "http://google.com" # Change another hstore property
user.first_name_changed?
=> true # should return false since website_url was changed
If I try to define an individual hstore field as a primary key in a has_many
association the generated SQL is just searching for NULL
(Postgres). Is it possible to support this use-case, or is there at least a work-around?
Could you explain why time is serialized as integer not as string? There is no place where timezone can be stored with time. With string serialization we can keep timezone with time/date in database.
Hi,
I use arrays pretty extensively for tagging IDs of associated records. hstore_accessor worked great for this. Why has it been removed and what should we be doing instead? Thanks.
class StripePaymentPlan < PaymentPlan
hstore_accessor :metadata,
stripe_id: { data_type: :string, store_key: 'sid' }
validates_uniqueness_of :stripe_id
end
Trying to call save
, I get something like:
NoMethodError: undefined method `limit' for nil:NilClass
activerecord-4.1.8/lib/active_record/validations/uniqueness.rb:65:in `build_relation'
It seems ActiveRecord actually attempts to look up the column rather than checking the hstore column, any good ways to handle this?
date_select pass the params like
"birth_date(1i)"=>"1980", "birth_date(2i)"=>"1", "birth_date(3i)"=>"1"
in model
hstore_accessor :profile,
birth_date: :time,
how can i save the birth_date?
Hey guys,
The behaviour I expected when using an array is that if it's empty to return [] and not nil. The same behaviour for hash but returning {}.
I tried to fork it and set a pull request with the following test:
context "when assigning values it" do
let(:product) { Product.new }
it "correctly sends an empty array to start with" do
expect(product.tags).to eq []
end
it "correctly sends an empty array to start with" do
expect(product.reviews).to eq({})
end
end
When I tried to push a branch I got:
git push origin empty_array_instead_of_nil
error: The requested URL returned error: 403 while accessing https://github.com/devmynd/hstore_accessor.git/info/refs
fatal: HTTP request failed
I've got an error when using the boolean typecast within Rails 5.0.0.beta3. All other types (string, datetime) work very well.
NoMethodError: undefined method `type_cast_from_user' for #<ActiveModel::Type::Boolean:0x007fc070def5c0>
from /usr/local/var/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/hstore_accessor-1.0.3/lib/hstore_accessor/active_record_4.2/type_helpers.rb:27:in `cast'
from /usr/local/var/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/hstore_accessor-1.0.3/lib/hstore_accessor/serialization.rb:26:in `block in <module:Serialization>'
from /usr/local/var/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/hstore_accessor-1.0.3/lib/hstore_accessor/serialization.rb:45:in `deserialize'
from /usr/local/var/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/hstore_accessor-1.0.3/lib/hstore_accessor/macro.rb:76:in `block (3 levels) in hstore_accessor'
from /usr/local/var/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/hstore_accessor-1.0.3/lib/hstore_accessor/macro.rb:80:in `block (3 levels) in hstore_accessor'
from (irb):7
from /usr/local/var/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/railties-5.0.0.beta3/lib/rails/commands/console.rb:65:in `start'
from /usr/local/var/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/railties-5.0.0.beta3/lib/rails/commands/console_helper.rb:9:in `start'
from /usr/local/var/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/railties-5.0.0.beta3/lib/rails/commands/commands_tasks.rb:78:in `console'
from /usr/local/var/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/railties-5.0.0.beta3/lib/rails/commands/commands_tasks.rb:49:in `run_command!'
from /usr/local/var/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/railties-5.0.0.beta3/lib/rails/command.rb:20:in `run'
from /usr/local/var/rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/railties-5.0.0.beta3/lib/rails/commands.rb:18:in `<top (required)>'
from bin/rails:9:in `require'
from bin/rails:9:in `<main>'
Thanks!
I was trying to nest an array as a content of the hstore value. What are the reasons for the deprecation? Is there better design decision rather that having a nested array? I find it quite useful in my use cases. Thanks for the clarification in advance!
First let me say that this is a great gem. I found it today and it's really nice and makes hstore use much cleaner.
I need to store DateTime values in an hstore column and defining as follows:
hstore_accessor :properties,
queued_at: :datetime,
started_at: :datetime,
finished_at: :datetime
and setting queued_at, started_at, and finished_at drops off the millisecond portion of the DateTime value. I had gotten this to work in my model with the following and was looking forward to removing this code:
properties["queued_at"] = qd_value.strftime("%Q").to_i
and reconstituting using:
Time.strptime(qd_value.to_s, '%Q').to_datetime
This preserved the millisecond value of the timestamp.
Gem version is 2.0.0 but rubygems.org is far behind - any chances for update?
Say I have a User
model with an hstore field called settings
that contains a boolean called notifications_enabled
. That allows me to do
[34] pry(main)> User.last.notifications_enabled
=> true
But if I didn't want to load the User
model into an instance is there a way to utilize hstore_accessor
to pluck out just the reminders_enabled
value? I can currently do something like:
[35] pry(main)> User.where(id: 1).pluck("settings->'notifications_enabled'")
=> ["true"]
But that is less than ideal because we lose the translation of the string "true"
to the value true
and I'd rather not hardcode the Postgres "settings->'notifications_enabled'"
query.
Ideally we could do something like:
User.where(id: 1).pluck(:notifications_enabled)
=> [true]
Hi,
In my model Order I have an hstore setup as:
hstore_accessor :refund, refunds: :array
I can set an array with no trouble:
order.refunds = [123456]
=> [123456]
But I cannot push new values to an array
order.refunds.push 44332211
=> [123456,44332211]
order.refunds
=> [123456]
Same with the << syntax
Am I missing something basic here? If the class of the column is Array, I think .push and << should work... otherwise this is not too terribly useful? I would need to save the old array to some temp variable, push to that, and then set the new array?
Anyone else see this behaviour with arrays?
How do I define the label in yml file?
I tried so.
....
model_name:
attribute: 'name'
also
....
model_name:
hstore_field/attribute: 'name'
But I could not, is there any way?
hstore_accessor marks the column as changed when set, even if the value is the same. It should only be marked changed if the value is different than the current value.
> user = User.first
> user.changed?
=> false
> user.show_labels
=> true
> user.show_labels = true
> user.changed?
=> true
> user.changes
=> => {"show_labels"=>[true, true]}
TLDR; A little more metadata please to ease the migration to HStorified data.
When writing generalised code (i.e. a model concern) it's sometimes necessary to apply some kind of check across all attributes in a model. This often requires that the hstore_accessors get some special treatment. Without knowing in advance what our hstore_accessor attributes names are this can only be inferred by searching all 900 or so methods for start_with?('hstore_metadata_for')
. Not so efficient when unnecessarily re-called once for every instance. This calls for a pre-load!
Could this be collected into a class attribute reader on the model named something like hstore_accessible_attributes
? Just a flat array of attribute names would be enough. e.g.
class MyModel
hstore_accessor :my_data,
dongle_time: :datetime,
dongle_what: :string
hstore_accessor :separate_things,
spangle_quantity: :decimal,
spangle_how: :string
end
MyModel.hstore_accessible_attributes # --> [:my_data, :separate_things]
Possible objections I could imagine
person.update(flat_params)
.Example
At work we have a concern which uses attributes.each
. We then refactored one of the models which includes that concern to migrate many of those attributes into an hstore column. attributes.each
no longer does the job. attributes_hstore_flattened.each
would have done the job. Perhaps a method like that would be good, perhaps it's going too far.
In any case, we could have neatly done that flattening ourselves if armed with the class instance var requested here.
So to come back to what's wrong with a list of setters? It's not the same thing as a list of persisted flattened attributes and we need a little more metadata to build that.
3 . Adding class instance vars is risky risky business
It's good enough for @columns_hash
, @default_attributes
, @table_name
, etc.. they serve a very similar type of role.
Cheers for reading. Now here's where you point out some stupid oversight I've made.
I just tried installing this gem on windows 8.1, and got the following error:
PS C:\> gem install hstore_accessor
ERROR: While executing gem ... (Errno::EINVAL)
Invalid argument - C:/RailsInstaller/Ruby2.0.0/lib/ruby/gems/2.0.0/gems/hstore_accessor-
1.0.0/lib/hstore_accessor/active_record_<_4.2
Any ideas?
Edit: I'm guessing it's because of these lines
Is it possible/beneficial to have a macro for querying the hash type?
I'm trying to figure out how to do it now, coming up unsuccessful and wondering if this is even possible to query with postgresql.
I have a User with a hstore_accessor :fee_list, :hash type, where fee_list is a hash like:
{"pickup dry cleaning" -> 15, "walk dog" -> 20}
Is it possible to query for Users where "pickup_dry_cleaning" is a key in the fee_list? And even better if I could query on the value as well.
I'm fine dropping down to SQL directly to do this in need be, but having trouble figuring out how that would look as, so far as I can tell, you can't cast a result to a hash.
Thanks
as of this commit rails/rails@ad84512
rails supports arrays of hstores
It would be very useful to add this support here.
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.