Reference another fixture without using label rails - ruby-on-rails

I have this model called Region and Admin
# regions.yml
one:
name: test
two:
name: test2
# admins.yml
one:
name: admin1
two:
name: admin2
There's a column on admin.rb defined as json column (store_accessor: :region_ids). How can I reference regions on admins.yml?
# admins.yml
one:
name: admin1
region_ids: ?????

This is an updated answer using erb to get the id for the various regions.
This creates structure json and queries the database to get the first and second Region IDs. These would be loaded in to the DB from the regions.yml when you run your tests.
# admins.yml
one:
name: admin1
region_ids: >
[{"region_one_id":<%= Region.first.id.to_json.inspect %>},
{"region_two_id":<%= Region.second.id.to_json.inspect %>}]
I looked into Fixture label interpolation to get the id but can't figure it out with your table holding json instead of a single id for another model.
Personally I would pre-define your ids on your regions.yml and then directly reference them in your admins.yml. This will help ensure you know the IDs and you have accurate data you've structured to test against.

I asked my colleague and got the answer. This will work:
# admins.yml
one:
region_ids: <%= Region.pluck(:id) %>
rails will load regions.yml when it gets to this point.

Related

Rails Duplicate key error: How to tell Rails to continue with the ID from the database

I'm currently deploying a Ruby on Rails web application with Postgres. I'm working with Docker, just to say it.
When I deploy my application, I insert some predefined data into the database. When I want to create a new record, I get a duplicate key error.
Full error message:
ERROR: duplicate key value violates unique constraint "modelname_pkey"
DETAIL: Key (id)=(1) already exists
How can I solve this? How can I tell Rails to continue with the primary key from the last record?
You can check insert_all method from Rails 6. If you are in lower versions of Rails use activerecord-import gem.
In case of insert_all
First form the json
new_records = [{id: 1, name: 'steve'},{id: 2, name: 'george'}]
Model.insert_all(new_records)
This will insert records if its not already there and ignore if records are there.
In case of activerecord-import
new_records = [{id: 1, name: 'steve'},{id: 2, name: 'george'}]
Model.import new_records, on_duplicate_key_ignore: true
references:
activerecord-import
rails-6-insert_all
If you don't specify an ID the database will choose one for you.
person = Person.create!( name: "MacReady", thing: false )
If you need to reference a specific ID, reconsider that. Relying on special IDs is too fragile, as you're discovering. Database IDs should be considered unique identifiers with no further special meaning.
For example, instead of remembering that "user ID 1 is the admin user" add an "admin" field.
admin = User.create!( name: "Yarrow Hock", admin: true )
Now you can check if user.admin for any user, have as many admins as you want, and change whether a user is an admin at any time.
I think this can also resolve your problem. Add this in your staging or preprod console (I am using Heroku for this so I added in my heroku rails console):
connection = ActiveRecord::Base.connection
connection.execute("SELECT setval(pg_get_serial_sequence('table_name', 'id'), MAX(id)) FROM table_name;")
Check out this blog for more info and how this is been generated.

ActiveRecord query with group and average

I have two models in a Rails application: a home has many speed_tests.
Every speed_test has as attributes: hostname and download.
I want to group hostnames and display the average of the download.
First of all I filter by city:
sts = SpeedTest.joins(:home).where("homes.city": city)
I have a collection of speed_tests, now I want to group them by hostname and get the average of download for every hostname
result = sts.select("hostname, AVG(download) as avg_download").group(:hostname)
The results are as following:
=> #<ActiveRecord::Relation [#<SpeedTest id: nil, hostname: "46.128.35.112.dynamic.cablesurf.de">]>
Why I can't see the column avg_download? Am I force to use the column speed_test id? I am not interested in this attribute.
You can't see it because that field is not part of your model, it's a virtual attribute. Anyway, it's present. Just call it. Try
result.map(&:avg_download)

Ruby on Rails SQLite3 Table ID numbers

I am practicing with "Rails: Up and Running" book. There is written that database fills id attributes by itself if not added. Here's an example.
some_items.yml:
one:
name: stringOne
two:
name: stringTwo
Ok. Now, I'm making migration and loading data with "rake db:migrate" and "rake db:fixtures:load".
Then I am trying to check what table "some_items" contains. While sending SQL query to SQLite3 console I'm receiving output like this:
298486374|stringTwo|2015-04-06 14:00:33|2015-04-06 14:00:33
980190962|stringOne|2015-04-06 14:00:33|2015-04-06 14:00:33
I am wondering is SQLite3 fills ID with random numbers? And if so, why not in normal ascending order like 1, 2, ...? And again - if so, why Rails doesn't create ID attribute in YAML testing files to show it in proper order?
I am wondering is SQLite3 fills ID with random numbers?
No, Rails is doing it.
And if so, why not in normal ascending order like 1, 2, ...?
I believe that Rails does it for fixture in order to be as much platform-independent as possible and keep it simple.
And again - if so, why Rails doesn't create ID attribute in YAML testing files to show it in proper order?
If you want to have sequencial IDs, you can have them in your fixtures files:
one:
id: 1
name: stringOne
two:
id: 2
name: stringTwo

Extend a record as you would a class (Ruby on Rails)

I have an unusual need for my application.
I want users to be able to set defaults that their users can extend to make their own options
Basically, users will customize their own control panels for use by their end users.
Example
John is my user, he creates two defaults:
name: 'Age'
value: 21
and
name: 'Subscribe to newsletter'
value: false
To be clear, this is being created by my user, John in my Rails application. I am not hardcoding the above data in my model. The default is the model and it has a name and a value
Now Suzie will see her own version of John's control panel with these two defaults and extend them with her own options
name: 'Age'
value: 18
and
name: 'Subscribe to newsletter'
value: false
I understand that ruby has its own native way to extend objects from classes, but creating a class from a record is hurting my brain. Is there an elegant way to do this?
I agree with the comment that you don't need to create any new classes.
Often, when dealing with default values, the behavior of Hash#merge is useful.
default_values.merge(user_values) will give you a single hash that contains all of the values, but where they conflict, will take those from the second hash. See Hash#merge.
Without knowing exactly what your database schema looks like, and assuming that john and suzie are User objects with a relationship to Defaults and Options, then it could be something like:
default_values = Hash[*john.defaults.map{ |d| [d.name, d.value] }.flatten]
user_values = Hash[*suzie.options.map{ |o| [o.name, o.value] }.flatten]
default_values.merge(user_values)

How to get ar_fixtures to load protected attributes?

I'm using ar_fixtures to seed data in a rails project (the procedure was written before seed arrived in 2.3.4)
It is all working fine, except I find that one column in particular is not getting populated (user_id). user_id is set in the yaml file, for example:
- !ruby/object:Service
attributes:
name: name
updated_at: 2009-10-14 11:50:36
provider_id: "1"
id: "1"
description: ""
user_id: "1"
created_at: 2009-10-14 11:47:01
attributes_cache: {}
But even though the related user object exists when running the import (Service.load_from_file), user_id is nil after import. Other foreign keys (like provider_id in the example above) are loaded correctly.
I am suspecting this is because user_id is protected in the model and this getting blocked during mass assignment.
Does anyone know if this is the case, and if so, how to get around the mass assignment protection? Of course, I want to leave mass assignment protection in place for the application at runtime.
Fixed! Answering my own question..
Simply requires a tweak of the attr_protected / attr_accessible setting prior to loading. e.g.
Service.attr_protected.delete "user_id"
Service.load_from_file
Or if the restriction is based on attr_accessible:
Service.attr_accessible :user_id
Service.load_from_file

Resources