How do you run "Rails Runner" in heroku? - ruby-on-rails

here is what I am trying to do: Find if anyone has tweeted about a specific course offered. If someone has indeed tweeted about it, I'd like to save that tweet to my Tweet Model and then display that tweet in the corresponding course page.
The scripts works locally by running rails runner get_tweets.rb but on Heroku it seems that the script gets executed but doesn't write to the database. In heroku I am running heroku run rails runner get_tweets.rb (using the Cedar stack).
def get_course_tweets
#courses = Course.all
#courses.each do |course|
url = course.url
tweets = Twitter.search(url, {:rpp => 100, :recent => true, :show_user => true})
tweets.each do |tweet_info|
unless Tweet.find_by_tweet_id(tweet_info.id).present?
tweet = Tweet.new
tweet.course_id = course.id
tweet.tweet_id = tweet_info.id
tweet.tweet_text = tweet_info.text
tweet.from_user = tweet_info.from_user
begin
tweet.save!
rescue => error
puts error
end
end
end
end
end
Edit:
The current error I get from rescue is the following:
PG::Error: ERROR: value "186306985577299969" is out of range for type integer : INSERT INTO "tweets" ("book_id", "course_id", "created_at", "from_user", "tutorial_id", "tweet_already_exists", "tweet_id", "tweet_posted_to_reviews", "tweet_text", "updated_at") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING "id"

As can be seen from your error
value "186306985577299969" is out of range for type integer
you need to use a different datatype (for tweet_id, I believe), presumably a BIGINT, which ranges from -9223372036854775808 to 9223372036854775807.
To do so in Rails you can pass :limit => 8 in your up migration:
change_column :tweets, :tweet_id, :integer, :limit => 8
Note that you should always do some sort of logging or reporting when you rescue, or else bugs like this become very difficult to track down because they silently get bypassed.

The Twitter API actually sends back two types of ID's for tweets and users etc., one is a number, where as the other is a string representation of the number. This is because it's a very real possibility that they will have so many tweets and users that the ID's can overflow in different implementations. If you use the String as your value when inserting it shouldn't happen anymore.
The field is called id_str

Related

typeerror: no implicit conversion of hash into string when calling model.save

I am using
ruby version 2.2.6
rails version 4.1.16
postgresql 11.10
gem json-1.8.6
Currently I am getting the error "typeerror: no implicit conversion of hash into string" when I am trying to save a model. This only happen for param that is using json datatype.
The database schema for the table:
create_table "jasons", force: :cascade do |t|
t.string "name"
t.json "description"
end
I already entered accessors for using json in the Jason.rb model like this.
class Jason < ApplicationRecord
:description, accessors: [:key1, :key2, :watapak]
end
and also whitelisted the params in the controller.
def jason_params
params.require(:jason).permit(:name, :description, :key1, :key2, :watapak)
end
and I can call new on the model successfully with
jason = Jason.new(name: "Jason John", key1: "abcd", key2: "efgh")
=> #<Jason id: nil, name: "Jason John", description: {"key1"=>"abcd", "ke...
then when I'm trying to call jason.save, it return's the error "typeerror: no implicit conversion of hash into string".
I tested the code on another environment, using the latest ruby 3.0 and rails 6.1 and everything works great without any error.
One thing that I notice is that the SQL command on both environment is different.
Rails 6.1
Jason Create (0.5ms) INSERT INTO "jasons" ("name", "description") VALUES (?, ?) [["name", "Jason John"], ["description", "\"--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess\\nkey1: abcd\\nkey2: efgh\\n\""]]
Rails 4.1.6
SQL (6.6ms) INSERT INTO "device_models" ("created_at", “name", "description", “updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["“created at", "2021-08-09 07:13:04.157826"], ["name", “Jason John"], ["description", "{\"key1\":\"abcd\",\"key2\":\"efgh\"}"], ["updated at", "2021-08-09 07:13:04.157826"]]
The full error is as in this image. Also it differs a bit with the question but the root of the problem is the same.
Do tell me if more information is needed. Also sorry if my question is gibberish as this is my first time posting question, I don't really know how to structure it to make it simpler to understand.
Please help T.T
I suspect that you may have intend to write store :description, accessors: [:key1, :key2, :watapak] which is a hack to store serialized data in a VARCHAR/TEXT columns and does not work with native JSON types. Both serialize and store are practically obsolete.
NOTE: If you are using structured database data types (e.g. PostgreSQL
hstore/json, or MySQL 5.7+ json) there is no need for the
serialization provided by .store. Simply use .store_accessor instead
to generate the accessor methods. Be aware that these columns use a
string keyed hash and do not allow access using a symbol.
class Jason < ApplicationRecord
store_accessor :description, :key1, :key2, :watapak
end

rails rspec duplicate key value violates unique constraint after adding unique index

I am working on a rails app, and I added a table. On that table, I wanted an index so to add one, I made a migration for adding a unique index. My migration looks like
class AddIndexToTable < ActiveRecord::Migration
def change
add_index :table, :user_id, unique: true
end
end
everything seemed fine, but now when I run my specs against my migration changes I now get the error
ActiveRecord::RecordNotUnique:
PG::UniqueViolation: ERROR: duplicate key value violates unique constraint "index_table_name_on_user_id"
DETAIL: Key (user_id)=(15) already exists.
: INSERT INTO "table_name" ("user_id", "day", "last_visited_on", "daily_rewards", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5, $6) RETURNING "id"
I have tried dropping the DB's, re-migrating, re-setting the pk_sequence with ActiveRecord::Base.connection.reset_pk_sequence!(table_name) and other fixes I could find, but I am still stuck on the same error.
Could anyone help me understand why I keep getting this error, and how it can be fixed? I can post more code if needed
You've got either a factory or a test that's trying to create multiple table records for the same user. Either that's going to be obvious looking in the test that's causing this, or perhaps it's an integration test and you don't have your test environment set up properly to clean out the DB between test cases.
For me it was the error that was I allowing the id in the controller params and that I had and let! create ... run at every test. So it tried to create again with the same id.
Removing id from allowed params fixed it.

Replacing Ckeditor with ActionText in a Rails 6 app

So I replaced CKEditor with ActionText in my rails 6 application, the upgrade to rails 6 and installation of action text was smooth.
I want to ask how I can be able to migrate the data from my model attribute to the newly established action text association (well not exactly migrate, I want to be able to display the old data and even be able to edit/update it).
For example, I have a description attribute in my model that was used with CKEditor before, now I have changed that field to a rich_text field like this: has_rich_text :description
So now all references to description simply query its rich_text association.
If I wanted to do something like this in my view, how can I achieve that?
#model.description (display if rich_text data is present) || #model.description (or try this if rich_text data is blank, also display nothing if both is blank)
I'd like to achieve this for show, edit, update and delete actions. Any idea how to make this work?
Why not just migrate the Ckeditor columns to the the new action_text table?
class ConvertCkeditorToActionText < ActiveRecord::Migration[6.0]
def up
action_text_rich_text_statement = ActiveRecord::Base.connection.raw_connection.prepare('action_text_rich_text_statement', <<-SQL)
INSERT INTO action_text_rich_texts (
name, body, record_type, record_id, created_at, updated_at
) VALUES ($1, $2, $3, $4, $5, $6)
SQL
Rails.application.eager_load!
transaction do
Post.all.each do |post|
ActiveRecord::Base.connection.raw_connection.exec_prepared(
'action_text_rich_text_statement', [
'body',
post.body,
"Post",
post.id,
post.created_at,
post.updated_at
])
end
end
end
def down
raise ActiveRecord::IrreversibleMigration
end
end

Dynamic Forms with HStore and rails 4

I was following this railscast, and finished the tutorial. Everything was working fine. Then I decided to use hstore instead of a serialized hash, and after setting up hstore, ran into a error:
PG::Error: ERROR: Syntax error near '!' at position 4 : INSERT INTO "products" ("product_type_id", "created_at", "properties", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id"
I googled, and found a similar SO question, but I'm using Rails 4, which supposedly doesn't need to use that gem anymore.
Here's my code:
The relevant portion of my form.html.haml looks like this
= f.fields_for :properties, OpenStruct.new(#product.properties) do |builder|
- #product.product_type.products.each do |product|
= render "products/fields/#{product.field_type}", field: field, f: builder
My Product model looks like this:
class Product < ActiveRecord::Base
belongs_to :product_type
serialize :properties
end
I can post more code if it will help. Thanks!
The Rails4 PostgreSQL driver for ActiveRecord is supposed to have native support for PostgreSQL's hstore type so you shouldn't need to use serialize at all. Try ditching the serialize.
BTW, a ! will appear in a YAML string when you attempt to serialize some objects to YAML:
"--- !ruby/object:SomeClassName ..."
and that ! could cause some problems if PostgreSQL was expecting to see an hstore string.

How should I parse a Facebook Oauth token's expire date to store in PostgreSQL?

I'm using OAuth to authenticate my app to access a user's facebook details using OAuth. Recently, I migrated the database to use PostgreSQL, and now am having trouble storing the token information when returned from Facebook.
The exact error is:
PG::Error: ERROR: invalid input syntax for type timestamp: "1350424800"
: INSERT INTO "authentications" ("created_at", "oauth_expires_at", "oauth_token", "provider", "uid", "updated_at", "user_id") VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING "id"
Strangely, this wasn't the case with SQLite, so it's clearly something to do with the format in which Facebook returns the expiry date ("1350424800"). Any thoughts on how I should parse this to make it work?
I'm not doing anything clever with the way it's created at present, simply:
def self.create_with_omniauth(auth)
create(uid: auth['uid'], provider: auth['provider'], oauth_token: auth['credentials'].token, oauth_expires_at: auth['credentials'].expires_at)
end
Any help greatly appreciated!
There is a problem is the variable type in postgres. If you have 'oauth_expires_at' as a 'datetime' you should convert the string timestamp to a date time. In Rails you can easily convert it with Time.at like this:
... oauth_expires_at: Time.at(auth['credentials'].expires_at)
rewrite your function as follows:
def self.create_with_omniauth(auth)
create(uid: auth['uid'], provider: auth['provider'], oauth_token: auth['credentials'].token, oauth_expires_at: Time.at(auth['credentials'].expires_at))
end
If you want even more flexibility and use it also with omniauth-twitter you do something like this. I'm using the rail's try method to gracefully handle the cases when there is no expires_at in twitter or other social media
class User < ActiveRecord::Base
has_many :authentications, dependent: :destroy
def create_authentication(oauth)
authentications.create!(
provider: oauth['provider'],
uid: oauth['uid'],
oauth_token: oauth['credentials']['token'],
oauth_secret: oauth['credentials'].try(:[],'secret'),
oauth_token_expires_at: oauth['credentials'].try(:[],'expires_at') ? Time.at(oauth['credentials'].try(:[],'expires_at')) : nil,
data: oauth.to_json.to_s
)
end
end
PG::Error: ERROR: invalid input syntax for type timestamp: "1350424800"
You have a nummeric Unix Timestamp value here – whereas the PostgreSQL data type timestamp obviously has to be given in another format.
And looking here http://www.postgresql.org/docs/current/static/datatype-datetime.html#DATATYPE-DATETIME-DATE-TABLE, you can see some of the accepted formats, but a nummeric Unix Timestamp does not appear to be listed there.
So you’ll have to convert/format the value into one of the accepted formats first; there might be a function designed to do this in PostgreSQL … (if not, Google should help).

Resources