I am upgrading one of my news app to Rails 6.0.0. While working around I got a problem using Rich Text. My app is pointing the rich text body field instead of the my existing table body field.
Is it possible to use the existing table text field for the rich text, so that I can edit the contents whenever I need it. Like for new posts I can use the action_text_rich_texts table but for the existing posts I want to use the existing table body field.
Assuming there's a content in your model and that's what you want to migrate, first, add to your model:
has_rich_text :content
Then create a migration
rails g migration MigratePostContentToActionText
Result:
class MigratePostContentToActionText < ActiveRecord::Migration[6.0]
include ActionView::Helpers::TextHelper
def change
rename_column :posts, :content, :content_old
Post.all.each do |post|
post.update_attribute(:content, simple_format(post.content_old))
end
remove_column :posts, :content_old
end
end
Refer to this Rails Issue comment.
ActionText's helper has_rich_text defines getter and setter methods for you.
You can redefine the body method again, providing ActionText with the value stored in the table using read_attribute:
class Post
has_rich_text :body
# Other stuff...
def body
rich_text_body || build_rich_text_body(body: read_attribute(:body))
end
Related
I'm trying to return json-formatted data from my Sinatra REST API. I currently have a bunch of associations set up, but I'm having trouble getting the views I want from my API despite getting them easily in Ruby.
For example, from my tables:
DB.create_table?(:calendars) do
primary_key :id
end
DB.create_table?(:schedules) do
primary_key :id
foreign_key :resource_id, :resources
foreign_key :task_id, :tasks
foreign_key :calendar_id, :calendars
end
In Ruby, I'm able to run a block like this and display all the info I need through my associations:
Calendar.each do |c|
c.schedules.each do |s|
puts "RESOURCE ##{s.resource_id}"
s.tasks.each do |t|
p t
end
puts
end
end
the c.schedules call works because my calendar model contains a one_to_many :schedules association.
Now, I'm wondering how this translates to my Sinatra API. In my simple GET route, I've tried many variations trying to get the schedules associated with a calendar, and convert it to JSON:
get '/calendars' do
c = DB[:calendar].first
c.schedules.to_json
content_type :json
end
... but I'll end up with an error like undefined method 'schedules' for {:id=>1}:Hash
So it looks like it's returning a hash here, but I've tried a bunch of stuff and haven't figured out how I'm supposed to work with my associations in Sinatra. How can I do this?
Thanks!
The reason your first block works but the second doesn't is because in the first case, you're using a Sequel model instance of class Calendar, whereas in the second case you're using a Sequel dataset.
When you iterate over Calendar.each do |c|, the c variable gets populated with an instance of a Calendar class Sequel model object. This object has relationship methods defined (one_to_many) and you're able to query schedules and run other model methods on it.
However, c = DB[:calendar].first gets you a Sequel dataset. This object is different than a model instance, it returns a standard Ruby hash (or an array of hashes).
You can change your 2nd block to use a model instead and it will get the result you want:
get '/calendars' do
c = Calendar.first # <=== CHANGE FROM DATASET TO MODEL
c.schedules.to_json
content_type :json
end
The problem about humanize (and probably titleize too i'm not sure) is that it breaks the view if the string is nil.
and then you'll have to .try(:humanize) instead, which is a big headache if you realized that you made that mistake way too late and you have to recheck every line of code at your views.
What i'm looking for is a way i could humanize and titleize strings before storing them into the database. That way, even if i'm looking straight into my database i will see the strings humanized and titleized.
You can create a method in your model which will take the attributes of this, and will transform as you want and/or you need, you can "fire" this every time you save a record using it as a before_save callback:
For instance, having a model User which attributes name and lastname I'm using the name and using titleize on it:
class User < ApplicationRecord
before_save :titlelize_names
def titlelize_names
self.name = self.name.titleize
end
end
The params will contain the attributes as the user has typed them, but they will be saved according to what the model says:
Processing by UsersController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"...", "user"=>{"name"=>"name", "lastname"=>"lastname"}, "commit"=>"Create User"}
(0.1ms) begin transaction
SQL (0.8ms) INSERT INTO "users" ("name", "lastname", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["name", "Name"], ["lastname", "lastname"]...]
Is the only one interaction you need to do before save the record, the view is untouched and the controller just receive it and makes what model says.
If by any change it occurs some problem at the moment of saving the record you can use throw(:abort) and "to force to halt" the callback chain.
def titlelize_names
self.name = self.name.titleize
throw(:abort)
end
In addition and depending on which method you're using with the before_save callback, you can add custom errors which will be available to use within your controllers, as for instance, an error identified by the key :titleize_names could be then handled in the controller.
def titlelize_names
errors.add(:titleize_names, 'An error has ocurred with the name attribute.')
self.name = self.name.titleize
throw(:abort)
end
If humanize and titlerize return an exception if value is nil, create an if statement, and you can make this process in one service or in one function model using before_save or before_create callbacks:
value.titleize unless value.nil?
You can use model callbacks to transform the data before saving it to the DB: http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html - you probably want before_save.
Also, if you want to ensure the record is never nil, add a validation as well.
Example:
class Example < ActiveRecord::Base
validates :name, presence: true
before_save :fixname
# Force titleize on :name attribute
def fixname
self.name = self.name.titleize
end
end
Now your model will have a method called fixname that you can call anywhere on your model instance as well as having it called before saving it to the DB.
Is it possible to specify the column for an attribute? I have something like:
NAME, COUNTRY
The database is quite large and I have over a thousand columns which are capitalized like this. I want to refer to them as so:
attr_accessible :name, :country
Where :name = column NAME. I'd prefer Model.name rather than Model.NAME. It isn't possible to downcase every column name in the structure file.
Here is an idea to do the way you preferred.
Command to generate migration: (In my example, im applying this on Posts table. Change according to your table name)
rails g migrate RenameColumnsOfPosts
Below is the migration up method. Here taking all the column names and for each one I'm applying rename_column to make it downcase.
class RenameColumnsOfPosts < ActiveRecord::Migration
def up
Post.columns.map(&:name).each do |column_name|
rename_column(:posts, column_name, column_name.downcase)
end
end
def down
#You can do the opposite operation here. Leaving on you
end
end
As i didnt run it personally, it might need some changes. So start with it and let me know if facing any problem.
Please write code inside of model ,
it's just for demostration code,after you get it and update as per logic :
This Code inside of model :
...
attr_accessor :name,:country
before_save :fill_save
..
#assign variable like ...
def fill_save
self.NAME= self.name
self.COUNTRY= self.country
end
....
I've created an association where Project has_many Tasks and Task belongs_to Project.
I've created the form in admin/tasks.rb
form do |f|
f.inputs "Details" do
f.input :title
f.input :project
end
f.buttons
end
Now in the edit Task page I hahe a dropdown menu where I can choose the project, but the entry are #<Project:0x00000...>.
How can I customize the entries in the dropdown to show the Project title field instead?
I'm a Rails newbie.
Active admin makes use of formtastic, under the hood formtastic loops through your model searching for a method like name, to_s, value, title, that returns a string.
At the moment you see the data entry itself, if you want formtastic to show the name, make sure you put something like
def name
return self.what_key_you_want_to_use
end
in your Project.rb model.
That should let formtastic show the name action instead of the model .to_s!
This solved it for me:-
In project.rb (Model) to make ActiveAdmin display properly in select dropdown use alias_attribute.
alias_attribute :name, :project_name (or whatever you named the field in your database)
tldr: You can define or alias :to_label on your model to customize the label used:
def to_label
"#{name} - (#{id})"
end
alias_attribute :to_label, :name
The library used by Rails: Formtastic, (or an alternative: Simple Form) uses the collection_label_methods to configure which fields are checked for deriving a label for your model.
Formastic defaults are: "to_label", "display_name", "full_name", "name", "title", "username", "login", "value", "to_s"
Simple Form defaults are: :to_label, :name, :title, :to_s
As most of these fields might already be used in your model, to_label or display_name seems to be the good candidates. I prefer to_label.
You can create a proc like such :
f.input :your_field, member_label: Proc.new { |p| "#{p.name}"}
Rails 3.0.1
Mongoid (2.0.0.beta.20)
Class Post
embeds_many :comments
field :comments_count
end
Class Comment
embedded_in :commentable, :inverse_of => :comments
end
I want to select the 10 most commented posts. To do that I need comments_count field in Post. But since my Comment is polymorphic (Post.comments, Message.comments etc) I don't want create inc callbacks in Post. What I wan't to do is create callback in Comment which will update comment_count field in Post.
I don't know how I can perform inc operation in embedded document on Field from parrent document and execute this callback from parrent document
Here is how to increment the Post from the embedded polymorphic Comment:
Class Comment
after_create :update_post_comment_count
def update_post_comment_count
if self._parent.class == Post
Post.collection.update( {'_id' => self._parent._id},
{'$inc' => {'comment_count' => 1}} )
end
end
end
I am pretty sure that this callback will execute whenever a new Comment is created so I don't think you need to worry about executing it from the parent document. Let me know if it works.
See this SO answer and this Github issue for more info on callbacks in embedded documents.