Rails "Data too long for column" error after truncation - ruby-on-rails

I have an app that creates text excerpts from body text entered into the model and it seems to work fine except, for some reason, when i try to enter one particular string into the body text.
in my blog_post model I have
t.string "excerpt", limit: 114
in my controller i am creating the excerpt string by doing this:
def create
#blogpost = Blogpost.new(blogpost_params)
#excerpt = #blogpost.body
#blogpost.excerpt = truncate(#excerpt, :length => 114)
respond_to do |format|
if #blogpost.save
etc,etc,
end
This seems to work fine most of the time but i entered the following text as a test
You know how they used to say It's #Sinatra's world, the rest of us are just living in it. - well, it's as true today as it was then. Check out Frank. When he gets out of a #chopper, dressed in a perfect lounge suit, #cocktail in hand, his #hat stays perfectly tilted. When I get out of a #chopper (and I'm not talking about once or twice but every single time I ever get out of a chopper) the spinning blades blow my hat all over the place. #Milliners should think about that and you should too the next time you're out hat shopping.
(sorry it's a bit long) I get the following error:
ActiveRecord::StatementInvalid in MoansController#create
Mysql2::Error: Data too long for column 'excerpt' at row 1....
It looks like the truncate isn't working for some reason.. Is it something to do with this text or have i missed something else?

I think you should remove the database restriction and handle this by using a setter that truncates to the wanted length by default. In you model add excerpt_setter to the attr_accessible list. And then define it like this
def excerpt_setter=(str)
self.excerpt = truncate(str, :length => 114)
end
def excerpt_setter
self.excerpt
end
And then in the controller
def create
#blogpost = Blogpost.new(blogpost_params)
#blogpost.excerpt_setter = truncate(#excerpt.body, :length => 114)
respond_to do |format|
if #blogpost.save
etc,etc,
end
Another thing: You can also define a excerpt method in your model and drop the field if there isnt any good reason to store a part of the body in another field.
include ActionView::Helpers::TextHelper # this is needed to make the truncate method avaiable in model
...
...
...
def excerpt
truncate(self.body, :length => 114)
end
If you dont need the data stored in the database for performence reasons this whould be my prefered solution.

Related

Why is my object getting serialized into a string not text in my Rails ActiveRecord DB?

I am currently trying to store a user object accessed from the Soundcloud API into my local rails ActiveRecord database. The object acts like a hash.
This is what the object/hash looks like:
#<SoundCloud::HashResponseWrapper avatar_url="https://i1.sndcdn.com/avatars-000296065573-ewlbh2-large.jpg" city="Brooklyn" country="United States" description="Raising The Bar Since 2007" discogs_name=nil first_name="Fool's" followers_count=7993682 followings_count=84 full_name="Fool's Gold" id=5636679 kind="user" last_modified="2018/03/09 19:40:55 +0000" last_name="Gold" myspace_name=nil online=false permalink="foolsgoldrecs" permalink_url="http://soundcloud.com/foolsgoldrecs" plan="Pro Plus" playlist_count=250 public_favorites_count=808 reposts_count=449 subscriptions=#<Hashie::Array [#<SoundCloud::HashResponseWrapper product=#<SoundCloud::HashResponseWrapper id="creator-pro-unlimited" name="Pro Unlimited">>]> track_count=1037 uri="https://api.soundcloud.com/users/5636679" username="Fool's Gold Records" website="http://smarturl.it/FoolsGoldSpotify" website_title="Spotify">
My ActiveRecord Schema.rb
ActiveRecord::Schema.define(version: 20180323143520) do
create_table "soundcloud_users", force: :cascade do |t|
t.string "user_name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.text "user_hash"
end
end
What I have tried:
Researching how to serialize objects in Ruby I found that I was supposed to make the field-to-be-serialized of type 'text'. I have made two different attempts at serializing then de-serializing the data, all of which have failed. Here is what I did:
1) Serialize as a Hash.
Code I tried:
class SoundcloudUser < ApplicationRecord
serialize :user_hash, Hash
end
I then get this Error:
TypeError in SoundcloudQueriesController#create
allocator undefined for Proc
2) Serialize using YAML:
Code I tried:
class SoundcloudUser < ApplicationRecord
YAML::dump(:user_hash)
end
This time I don't get an error. When I try to access the hash I wrote this code in my index def just to display it quickly:
def index
#user = SoundcloudUser.find(2)
#user_hash = YAML::load(#user.user_hash)
end
However, I for some reason cannot access the parameters the way I expect i.e. #user_hash.followers_count.
So I decided to go into rails console and see what was going on:
> #user = SoundcloudUser.find(6)
> #user_hash = #user.user_hash
> #user_hash.class
=> String
So for some reason, even though the field in active record is text, it is getting stored as a string? Or maybe the read back is converting it to a string? I really don't know what's going on, but am a bit lost. It's my first time serializing data, and the Soundcloud API has always returned weird objects that never work the way I expect. SUPER appreciate any help or advice!
EDIT #2:
Trying with JSON as per suggestion
When I changed my code as you prescribed, I still get the same issue:
SoundCloudQueriesController.rb
def index
end
def create
#user_url = params[:soundcloud_url]
#soundcloud_user = SoundcloudUser.new
#query = Query.new
#user = #query.query_user(#user_url)
#soundcloud_user.user_name = #user.username
#soundcloud_user.user_hash = JSON.parse(#user.to_json).symbolize_keys
#soundcloud_user.save
end
SoundcloudUser.rb (model)
class SoundcloudUser < ApplicationRecord
end
Output (querying the database in rails console):
Typing in the following commands into rails console:
#user = SoundcloudUser.find(7)
#user_hash = #user.user_hash
This gives the following output of the hash (which still seems to be weirdly formatted with the "\" that weren't there before):
=>
"{:id=>8, :kind=>\"user\", :permalink=>\"alex\", :username=>\"🔥𝔞𝔩𝔢𝔵 🔥\", :last_modified=>\"2018/03/15 18:15:56 +0000\", :uri=>\"https://api.soundcloud.com/users/8\", :permalink_url=>\"http://soundcloud.com/alex\", :avatar_url=>\"https://i1.sndcdn.com/avatars-000208970259-vngd3i-large.jpg\", :country=>\"Germany\", :first_name=>\"Alexander\", :last_name=>\"Ljung\", :full_name=>\"Alexander Ljung\", :description=>\"Hi, my name is Alex. I like Sound. Founder & Chairman, SoundCloud.\", :city=>\"Berlin/NYC\", :discogs_name=>nil, :myspace_name=>nil, :website=>nil, :website_title=>nil, :track_count=>268, :playlist_count=>34, :online=>false, :plan=>\"Pro Plus\", :public_favorites_count=>1601, :followers_count=>185322, :followings_count=>1697, :subscriptions=>[{\"product\"=>{\"id\"=>\"creator-pro-unlimited\", \"name\"=>\"Pro Unlimited\"}}], :reposts_count=>132}"
When I try to access the hash as you prescribed:
> #user_hash[:followers_count]
I get the following message:
TypeError: no implicit conversion of Symbol into Integer
from (irb):6:in `[]'
from (irb):6
Weirdly enough, it gives the same error even for fields that are not integer values like :followers_count (:username for example).
I think you are right, this is not a hash. So now I am super lost at how to store this in my database haha. I tried just storing it with no serialization, but I get the same issue of it being seemingly converted to a string.
I see in your source code:
class Query
def initialize
#client = Soundcloud.new(:client_id => API_KEY)
end
def query_user(user_url)
#user_url = user_url
#user = #client.get('/resolve', :url => #user_url)
end
end
At this point #user is a still a SoundCloud::HashResponseWrapper object.
If you wanna store the data as string text you'll need to first do:
#user.to_json
To save it with symbolized keys in the db:
data = JSON.parse(#user.to_json).symbolize_keys
sc_user = SoundcloudUser.new(user_hash: data)
But you still can't call .key on a hash. If you want to call the value of the key you'll need to do:
sc_user.user_hash[:username] #for example
There are ways to extend your model but that's out of scope for this question.
UPDATE: Here's your updated controller:
class SoundcloudQueriesController < ApplicationController
def new
end
def index
#user = SoundcloudUser.find(3)
#user_hash = #user.user_hash # but do you even need this?
end
def create
sc_user_data = Query.new.get_user params[:username]
#soundcloud_user = SoundcloudUser.create({
user_name: sc_user_data[:username],
user_hash: JSON.parse(sc_user_data.to_json).symbolize_keys
})
end
def show
end
end
You'll also need to modify your Query class to be able to build a user from the souncloud username. It will look like this:
class Query
BASE_RESOLVE_URL="http://soundcloud.com/"
def initialize
#client = Soundcloud.new :client_id => ENV['SC_CLIENT_ID']
end
def query_user(user_url)
#user_url = user_url
#user = #client.get('/resolve', :url => #user_url)
end
def get_user(username)
#client.get('/resolve', url: BASE_RESOLVE_URL+username)
end
end
I'm not exactly sure what you want to use the query_user method for, or if you can just remove it and use get_user instead. In any case I forked your repo, got the controller create test passing. Will send PR.

How do you insert a input name="title0" in the title column in the rails column?

How do you insert a input name="title0" in the title column in the rails column?
plz using params.require().permit()
#bank = Bank.new(params.require(:bank).permit(:title))
this is POST title => DB column title
but I want is POST title0 => DB column title
Sorry, i am bad at english
First, refactor your code as described here. So, your code will become
def probably_create_action
#bank = Bank.new(bank_params)
end
...
private
def bank_params
params.require(:bank).permit(:param1, :param2, :param3, :title0)
end
Attributes not listed inside bank_params method will not be passed.
UPDATE
Fighting with conventions is not a good practice. But below snippet might save your day.
#app/models/bank.rb
def title0=(inp)
self.title = inp
end

Indexing and ordering by dynamic field with sunspot

So I have many items that can be part of many different pages. So here is the simplified models:
class Page
#we just need the id for this question
end
class Item
embeds_many :page_usages
end
class PageUsage
field :position, :default => 0
embedded_in :item
belongs_to :page
end
So the page_usage is holding the position of the items on every page. I want to put that into solr so it can pull up the right items and in the right order for me.
I've looked into dynamic fields and ended up with something like this but not really sure. I want the field to basically be the page id pointing to the position of the item:
searchable do
dynamic_integer :page_usages do
page_usages.inject({}) do |hash, page_usage|
hash.merge(page_usage.page_id => page_usage.position)
end
end
end
And in my controller I have something like this:
Item.search do
dynamic :page_usages do
#i have #page.id but not sure how to get all items with the #page.id
end
end
I need something that will check if the item exist on the page and find out how to use order_by with the position. Is this possible this way or do I have to find another solution?
Solved it after lots of trial and error.
searchable do
dynamic_integer :page_usages do
page_usages.inject({}) do |hash, page_usage|
hash.merge( ("page" + page_usage.page_id.to_s).to_sym => page_usage.position)
end
end
end
So I first had to store the key as a symbol which is important. But the problem I ran into was that the symbol couldn't have quotes in it. So if you call to_sym on the id, it would look something like :"123456789" which will give you a "wrong constant name" error later on. So I threw on a string before the id to create the new symbol which looks like :page123456789.
Next step was to create the search block:
Item.search do
dynamic :page_usages do
with ("page" + page.id.to_s).to_sym ).greater_than(-1)
order_by(("page" + page.id.to_s).to_sym, :asc)
end
end
By using that page id, I was able to pull up all the right items in the right order. I used greater than -1 because by default my positions start at 0 and goes up from there.

Ruby: Formatting table data to JSON

I'm trying to make a timeline for an the bugs and updates for an open source project. I'm new to ruby, but I'm getting some experience gradually.
I've created a table called historical_gems, with the following code in the model:
class HistoricalGem < ActiveRecord::Base
attr_accessible :build_date, :version
belongs_to :ruby_gem, :foreign_key => :gem_id
end
I'm using a JS Plugin (http://almende.github.com/chap-links-library/js/timeline/doc) that requires objects with two field names ('start' for the date and 'content' for the title) in the JSON Array to display the timeline using JS.
I believe I have to do something like this in the controller which defines my timeline method to render the JSON:
def timelinem
#name = params[:id]
#rpm = AbcTable.find_by_name(#name)
respond_to do |format|
format.json { render :json => #rpm.json_timelines }
end
end
Then I probably would have to define a 'json_timelines' method inside my model, maybe something like:
def json_timelines(gems = [])
dates = []
gem_id.each { |p|
gems << p
dates << p.build_date(gems)
end
}
end
I'm only starting out with RoR, and even after hours with guides and tutorials and debugging, I'm not able to put together this code. Can anyone help me out, please? I don't think I'm doing it right.
btw, don't be too harsh if I overlooked something obvious, I'm only 16 :)
The render :json => ... in your code should work fine (but with HistoricalGem instead of AbcTable) as long as json_timelines returns an object that's serializable as JSON (e.g., an Array or a Hash).
Try something like this for your method definition:
def json_timelines(gems = [])
gems.map do |g|
{
:content => g.title,
:date => g.build_date
}
end
end
The above snippet assumes your "historical_gems" table has "title" and "build_date" columns; if not, adjust the code to reflect the fields you actually want represented in your JSON.

prawn pdf group, transaction and rollback method problems

I'm trying to create a pdf report using prawn in a rails application. There are lots of sections that contain user generated content that I want to try and group together. Sometimes this will go over more that one page which results in a cannot group error. I then tried to use a transaction so that in the event of an error I can rollback and then output the content without using the group method.
The problem is the rollback stuffs up the pages. It removes the extra page from the pdf but still has the wrong page count and outputs over lapping content when I try to redo it. I reset the y position after the rollback, as per the prawn documentation but I still get the problems.
eg. The following test code writes 2 pages of numbers, does a rollback to the start and then tries to write the same numbers again. It results in a single page pdf with the second page of numbers overlapping the first and a page count of 2. The page counts at the bottom of the page also overlap one another even though I'm using the prawn number_pages method
class TestReport < Prawn::Document
def to_pdf
font('Helvetica')
bounding_box([bounds.left, bounds.top - 50], :width => bounds.width, :height => bounds.height - 100) do
text 'begin'
y_pos = y
transaction do
begin
group do
64.times do|i|
text i.to_s
end
end
rescue
rollback
end
end
self.y = y_pos
64.times do|i|
text i.to_s
end
text 'end'
text page_number.to_s
end
page_numbers(1)
#render
end
def page_numbers(start)
string = "page <page> of <total>"
options = { :at => [bounds.right - 150, 40],
:width => 150,
:align => :right,
:start_count_at => start,
:color => "000000" }
number_pages string, options
end
end
def test_report
pdf = TestReport.new()
pdf.to_pdf
send_data pdf.render, filename: "test.pdf",
type: "application/pdf",
disposition: "inline"
end
The problems seem to be with transaction rollbacks. The main thing I want is to be able to use the group method. Is there another way?
Is my code wrong? Am I missing something or do transaction not currently work.
I'm currently using the master prawn branch in a ruby on rails application ( gem 'prawn', :git =>
'git://github.com/prawnpdf/prawn.git', :branch => 'master').
This question is quite old now, but I'll post an answer since it is one of the first hits on Google when searching for the exception.
Transactions still doesnt work with page breaks (v 1.0.0.rc2), so I created a helper method that tries to use grouping first and then if the exception occurs it just retries without grouping, making the content span more than one page.
def group_if_possible(pdf, &block)
begin
pdf.group { block.call }
rescue Prawn::Errors::CannotGroup
block.call
end
end
Example: Using it when creating a table:
group_if_possible(pdf) do
pdf.table(rows)
end
EDIT:
Grouping were removed from Prawn 1.x but there is an unofficial grouping gem that works well for Prawn 2:
https://github.com/ddengler/prawn-grouping
Looks like Brad Ediger answered your question on Google Groups, but for the benefit of anyone else looking for help with this, here's his response:
Sadly, transactions do not yet work correctly when they start new
pages or change the pages collection. It's a known issue:
https://github.com/prawnpdf/prawn/issues/268
-be

Resources