I have created an app in Rails 4.2.3
I am trying to seed the database but after I do rake db:migrate I am getting nil values in the field of the database table. The only piece of code I have in my seeds.rb is:
User.create![{username: "test", password_digest: "test"}]
In my schema.rb I have:
create_table "users", force: :cascade do |t|
t.string "username"
t.string "password_digest"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
My User model is:
class User < ActiveRecord::Base
has_one :profile, dependent: :destroy
has_many :todo_lists, dependent: :destroy
has_many :todo_items, through: :todo_lists, source: :todo_items
end
There are other people that experienced the same problem like here and here but I don't think that the solutions apply to my case as I do not have any attr_accessor in any of my models.
Anyone knows why that might be happening?
brace-types matter...
use the following instead
User.create!( username: "test", password_digest: "test" )
This tells rails to create a single user, using the hash that contains username "test" and password "test"
what you have is telling rails to create a user with an array [] that contains a hash {}... but create is not expecting an array of hashes - it's expecting just a single hash.
EDIT: re: using []
That's because when you don't use () but do use [], then ruby has to guess what you mean...
are you passing an array to a method or are you calling the array-indexing method on a variable? (or hash-indexing if it's not an integer in the brackets)?
If you put the [] right next to the word create! (eg User.create![]) ruby will probably interpret that as the latter... eg it's looking for an variable on the User class called create!... and then tries to look for a key of that hash called {username: "test", password_digest: "test"}, {username: "test2", password_digest: "test2"} and then it gets confused because when you use the array-indexing method [] you should pass it only one key to find, not two ({username: "test", password_digest: "test"} is the first and {username: "test2", password_digest: "test2"} is the second)... thus giving you the error that you're trying ot pass 2 arguments instead of one... but also just using the wrong interpretation to begin with.
neither of which is what you want...
If you leave a space between the [] and create! then ruby is more likely to interpret the [] as a parameter being passed to a method... and then it triggers a different error.
To be unambiguous - always use a space... or use parens () when passing an argument that could be ambiguous in this way.
Related
I have a Rails app (rails v6.0.3, ruby 2.7.1) that is using the Noticed gem to send notifications. I have the following model configuration:
class Vendor < ApplicationRecord
has_noticed_notifications
end
The has_noticed_notifications is, as described in their README, a "Helper for associating and destroying Notification records where(params: {param_name.to_sym => self})"
So when I create a Notification like so...
VendorAddedNotification.with(
vendor: vendor,
data_source: "user",
).deliver(some_user) # => Notification inserted!
I expect to be able to find the Notifications that reference the vendor, using the Noticed method, like so:
vendor = Vendor.find ...
vendor.notifications_as_vendor # => Expected: [ Notification#123 ]
However, the input is always an empty array (Actual => [])
I looked at their source code and it looks like notifications_as_vendor is the following query:
Notification.where(params: { :vendor => self }) # where self = an instance of the Vendor model
However, that doesn't seem to work, and I'm not sure if it's supposed to or not. I tried running a simpler query to see if it worked ...
Notification.where(params: { :data_source => "user" })
But that did not work either. However, when I ran the same query with a different signature, it did:
Notification.where("params->>'data_source' = ?", "user")
So my question is-- is this Notified's mistake, or am I missing something in my configuration? I'm using PSQL for this, here is the relevant schema:
...
create_table "notifications", force: :cascade do |t|
t.string "recipient_type", null: false
t.bigint "recipient_id", null: false
t.string "type", null: false
t.jsonb "params"
t.datetime "read_at"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["read_at"], name: "index_notifications_on_read_at"
t.index ["recipient_type", "recipient_id"], name: "index_notifications_on_recipient_type_and_recipient_id"
end
...
And here are the related models:
class VendorAddedNotification < Noticed::Base
deliver_by :database
param :vendor
param :data_source
end
class Notification < ApplicationRecord
include Noticed::Model
belongs_to :recipient, polymorphic: true
end
Thank you in advance!
I've found why it's not working, it seems to be an issue with Notified.
In plain SQL I ran:
# PLAIN SQL
select "params" from "notifications" limit 1
Which returns the notification's params (returned notifcation's id=77)
# PLAIN SQL Result
"{""added_by"": {""_aj_globalid"": ""gid://stack-shine/WorkspaceMember/269""}, ""data_source"": ""user"", ""_aj_symbol_keys"": [""workspace_vendor"", ""data_source"", ""added_by""], ""workspace_vendor"": {""_aj_globalid"": ""gid://stack-shine/WorkspaceVendor/296""}}"
Now in Rails when I do
vendor = Notification.find(77).params[:vendor]
vendor.notifications_as_vendor.to_sql
The result is ...
"SELECT \"notifications\".* FROM \"notifications\" WHERE \"notifications\".\"params\" = '{\"vendor\":{\"_aj_globalid\":\"gid://stack-shine/Vendor/296\"},\"_aj_symbol_keys\":[\"vendor\"]}'"
... the extracted params from that query are:
'{\"vendor\":{\"_aj_globalid\":\"gid://stack-shine/Vendor/296\"},\"_aj_symbol_keys\":[\"vendor\"]}'
So ... In the database, the serialized params are A, but Rails is search for B:
# A: `params` In the database
"{""added_by"": {""_aj_globalid"": ""gid://stack-shine/WorkspaceMember/269""}, ""data_source"": ""user"", ""_aj_symbol_keys"": [""vendor"", ""data_source"", ""added_by""], ""vendor"": {""_aj_globalid"": ""gid://stack-shine/Vendor/296""}}"
# B: `params` Searched with by Rails
"{\"vendor\":{\"_aj_globalid\":\"gid://stack-shine/Vendor/296\"},\"_aj_symbol_keys\":[\"vendor\"]}"
Clearly this query could not work because the params in the database are not the params being search by Rails.
The notification, in the database, has extra parameters on top of "vendor" ("data_source" and "added_by") that are not being search up by the Vendor. Is this why it returns nothing?
For now, I'll simply the look up the notifications myself by storing the vendor_id in params and doing something like Notification.where("params >> vendor_id = ?", 123)
I have a table called 'products' (model is Product) and I can't access the :uuid attribute when running in migration context. The migration itself does not change the structure but accesses and creates new objects to populate the DB.
This is a snippet of the schema.rb prior to the migration:
create_table "products", force: :cascade do |t|
t.string "title"
t.string "description"
t.string "price"
t.uuid "uuid"
end
The Product object is defined as follows:
class Product < ActiveRecord::Base
end
Now when running in rails console / code this works fine:
p = Product.create!(:uuid => xxx)
puts p.uuid # => xxx
puts p.inspect # => Product(title: string, description: string, price: string, uuid: uuid)
However while running in the migration context - the same code raises an exception:
p = Product.create!(:uuid => xxx)
puts p.uuid # => undefined method `uuid' for <Product:0x007fea6d7aa058>/opt/rubies/2.2.0/lib/ruby/gems/2.2.0/gems/activemodel-4.2.3/lib/active_model/attribute_methods.rb:433
puts p.inspect # => Product(title: string, description: string, price: string)
The uuid attribute is missing! What's wrong?
put
Product.reset_column_information
before your Product.create line.
The schema of the model is generally refreshed after the migration. Therefore, even if the uuid field is created, the model doesn't have knowledge of it yet.
You can force a refresh by using
Product.reset_column_information
However, the issue in your code reveals you are probably using the migration feature to create records inside the migration itself. This is generally not recommended as the migrations are designed to change the schema of your database, not the data.
You should use create a specific rake task to modify the data and run the task after the migration is completed, for example from the console.
I'm working in rails 4.1 with a postgres backend, handled by the 'pg' gem (0.17.1). When I try to update and save some plain integer/string fields, the change seems to be successful but doesn't get written to the DB. No errors are thrown anywhere (that I've found).
A schema excerpt can be found below:
enable_extension "plpgsql"
create_table "widgets", force: true do |t|
t.string "id"
t.integer "recipient"
t.datetime "receivedAt"
end
In the rails console:
w = Widget.find('14b13b1e')
=> #<Widget id: "14b13b1e", receivedAt: nil, created_at: "2014-10-04 17:10:39", updated_at: "2014-10-04 17:10:39">
w.receivedAt = Time.now()
=> 2014-10-04 19:39:33 +0100
w.save()
=> # database commit runs and returns 'true'
Widget.find('14b13b1e')
=> #<Widget id: "14b13b1e", receivedAt: nil, created_at: "2014-10-04 17:10:39", updated_at: "2014-10-04 17:10:39">
I've also tried this using Widget.update, Widget.update_attribute(), and Widget.receivedAt_will_change!(). In every case, I get the same result. There are no errors to be seen:
w.errors.messages
=> {}
This is happening with all fields that I try to change.
Where else can I look to find the error?
Edit: sorry, I edited down the randIDs because it was a lot of useless text, and forgot to edit one occurrence. Fixed.
I also forgot to mention that I'm using a nonstandard primary key. In my Widgets model, I have
self.primary_key = "randID"
Widgets still have a normal 'id' field, but it can only be accessed with Widget['id'] (because Widget.id now returns the randID). I haven't had any problems with selecting Widgets.
Sigh. It's totally my fault. Here's an excerpt from my model file, widget.rb
class Widget < ActiveRecord::Base
self.primary_key = "randID"
before_save :set_randID
belongs_to :user
...
end
I was running a function to calculate the primary key (set_randID) BEFORE SAVE (instead of just once before_create, as I should have done). When I was trying to update an instance of the model, the save would try to recalculate and set the primary key on the instance object.
This seems to have failed silently all the way up the stack. The SQL query looks like it returned "true" and the ActiveRecord object had an empty errors hash.
So, issue is fixed; totally my fault, sorry for wasting your time...but does anyone have an idea where I could have looked to notice this earlier?
I am using Ruby on Rails 4 and I have this User model:
require 'uuid'
UUID.state_file = false
UUID.generator.next_sequence
class User < ActiveRecord::Base
attr_accessor :email, :password
has_many :entries
after_initialize do |user|
user.entry_hash = UUID.new.generate
end
end
Which is based on the following DB migration:
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string "email"
t.string "password"
t.string "entry_hash"
t.timestamps
end
end
end
I want to automatically generate a uuid as a hash associated with that User.
Using the rails console to create a new User, I do the following:
1) I use create a User with an email and password:
irb(main):001:0> u = User.create(:email=>'abc#gmail.com', :password=>'myPass')
(0.1ms) BEGIN
SQL (0.6ms) INSERT INTO `users` (`created_at`, `entry_hash`, `updated_at`) VALUES ('2013-12-07 22:32:28', '60744ec0-41bd-0131-fde8-3c07542e5dcb', '2013-12-07 22:32:28')
(3.7ms) COMMIT
=> #<User id: 2, email: nil, password: nil, entry_hash: "60744ec0-41bd-0131-fde8-3c07542e5dcb", created_at: "2013-12-07 22:32:28", updated_at: "2013-12-07 22:32:28">
2) But this is weird. If you look at the SQL, it doesn't insert anything but the entry_hash, and the output object shows email and password as nil. However, when I try to access those properties, I get the ones I put.
irb(main):002:0> u.email
=> "abc#gmail.com"
irb(main):003:0> u.id
=> 2
irb(main):004:0> u.password
=> "myPass"
irb(main):005:0> u.entry_hash
=> "60744ec0-41bd-0131-fde8-3c07542e5dcb"
I am very new to Ruby on Rails and I know some magical stuff goes on in the background, but can someone enlighten me as to whats going on here? I just want to create an object with parameters.
Cheers
UPDATE:
I fixed the problem I was having by removing the attr_accessor line. Anyone know why that made it work?
attr_accessor
Can be used for values you don't want to store in the database directly and that will only exist for the life of the object (e.g. passwords).
Is used when you do not have a column in your database, but still want to show a field in your forms. This field is a “virtual attribute” in a Rails model.
The method create creates the row in the database and also returns the ruby object. That is why accessing the fields through the variable u worked, it is alive while the console is open. However, nothing made it to the database.
I'm exploring Rails for the first time and trying to add some fairly straightforward role-based security to my test app. Some Googling seemed to indicate rails-authorization is the way to go for this. I followed the README and everything seemed to be going well, but now I'm trying to associate a User with a Role and it fails. Here's the snippet from my script/console session:
>> u = User.find(:first)
=> #<User id: 1, login: "cwhit", name: "", email: "cwhitfield#unica.com", crypted_password: "7ac064547fb8992e8e53e936df31657a40f9c5af", salt: "56671492059f8e40eb3d509940944aaba31ebc72", created_at: "2009-03-26 18:06:04", updated_at: "2009-03-26 18:06:04", remember_token: nil, remember_token_expires_at: nil>
>> r = Role.find(:first)
=> #<Role id: 1, name: "ProjectManager", authorizable_type: nil, authorizable_id: nil, created_at: "2009-03-27 11:02:35", updated_at: "2009-03-27 11:02:35">
>> u.has_role r
ActiveRecord::StatementInvalid: PGError: ERROR: column "id" does not exist
LINE 1: ....546623', '2009-03-27 11:42:16.546623', 5, 1) RETURNING "id"
^
: INSERT INTO "roles_users" ("created_at", "updated_at", "role_id", "user_id") VALUES('2009-03-27 11:42:16.546623', '2009-03-27 11:42:16.546623', 5, 1) RETURNING "id"
Am I just doing something silly, or is this a known issue? I found essentially the same error in a question in the Google Group for the rails-authorization plugin, but there was no solution provided.
Here's my basic config:
OS X
Rails 2.3.2
PostgresQL
Plugins:
restful-authentication
rails-authorization
Ran into the same problem with models where I was using a primary key other than 'id'.
Turns out I had forgotten to declare it in the model class definition using "set_primary_key". Perhaps you need to do something like this:
class CulpritClass < ActiveRecord::Base
set_primary_key :key_field_you_want
end
Try installing the composite_primary_keys gem (link) and add the following to the RolesUser controller:
set_primary_keys :user_id, :role_id
Since there is no id field for that table (and there doesn't need to be), the primary key becomes (user_id, role_id) which you can add to the database with
ALTER TABLE roles_users ADD PRIMARY KEY (user_id, role_id);
This solved the problem in my case, which seems to be Postgres-specific.
I simply used two primary key declarations
set_primary_key :user_id
set_primary_key :role_id
This worked for my purposes but keep in mind that it returns (in the sql at least) the second primary key that you define. In this case it would be the role_id of the object.
I had the same problem but on destroy (of roles). I tried the composite_primary_keys gem but found that it broke other things in Rails 2.3.4. Specifically, it caused Rails :belongs_to association to generate the wrong attributed id in this situation:
belongs_to :inviter, :class_name => 'User'
It was generating user_id as the attribute name instead of inviter_id.
The ultimate solution was to add an id column to roles_users. Since I already had a roles_users table with no surrogate key I had to do this lil migration:
class AddIdToRolesUsers < ActiveRecord::Migration
def self.up
# create new table the way it should be (_with_ a traditional Rails id column)
create_table :x_roles_users do |t|
t.belongs_to :role
t.belongs_to :user
t.timestamps
end
execute 'insert into x_roles_users (user_id, role_id, created_at, updated_at) select * from roles_users'
drop_table :roles_users
rename_table :x_roles_users, :roles_users
end
def self.down
remove_column :roles_user, :id
end
end