I need to save processed data inside my model to render it as json but i get that the method is missing so time for a stupid question.
The model
class Post < ActiveRecord::Base
def self.html(html)
#html = html
end
end
The controller
# POST /posts
# POST /posts.json
def create
#post = Post.new(post_params)
respond_to do |format|
if #post.save
#post.html render_to_string(partial: 'post.html.erb', locals: { post: #post })
format.html { redirect_to #post, notice: 'Post was successfully created.' }
format.json {
render :show,
status: :created,
location: #post
}
else
format.html { render :new }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
The error
NoMethodError - undefined method `html' for #<Post:0x0000000a5679d0>:
That's because in the builder i would like to output
json.extract! #post, :id, :content, :created_at, :updated_at, :html
I could probably do this in another way but now i'm curious, what am i missing?
Just add regular getter/setter:
class Post < ActiveRecord::Base
def html
#html
end
def html=(html)
#html = html
end
end
You also probably want an instance method, because you are working with instance of Post (you called Post.new earlier.
When you define the method html on the post model, you are creating a class method, and not an instance method. You need to remove the self and make it a setter by adding =:
class Post < ActiveRecord::Base
def html=(html)
#html = html
end
end
Related
I am a newbie in RoR, thus sorry for stupid question :(
I have a Game model, with a code string. There is a welcome/index view in my app with a simple form_to input. I wish to redirect user to a Game with a specific code after he submits the form.
I understand that I should somehow combine a .where method and redirect_to in Welcome_controller, but I just can't figure out how...
Welcome_controller.rb:
class WelcomeController < ApplicationController
def index
end
def redirect
redirect_to ?game with a code that equals :param from input?
end
end
Welcome/index:
<h1>Let's join the game!</h1>
<%= form_tag redirect_path do %>
<%= text_field_tag(:param) %>
<%= submit_tag("Search") %>
<% end %>
routes.rb:
Rails.application.routes.draw do
get 'welcome/index'
resources :games
get 'games/index'
root 'welcome#index'
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
game.rb:
class Game < ApplicationRecord
validates :name, :presence => true
end
games_controller:
PREFACE = ('A'..'Z').to_a << ?_
SUFFIX = ('0'..'9').to_a
PREFACE_SIZE = 2
SUFFIX_SIZE = 3
class GamesController < ApplicationController
before_action :set_game, only: %i[ show edit update destroy ]
# GET /games or /games.json
def index
#games = Game.all
end
# GET /games/1 or /games/1.json
def show
end
# GET /games/new
def new
#game = Game.new
#game.code = gen_name
end
def gen_name
PREFACE.sample(PREFACE_SIZE).join << SUFFIX.sample(SUFFIX_SIZE).join
end
# GET /games/1/edit
def edit
end
# POST /games or /games.json
def create
#game = Game.new(game_params)
respond_to do |format|
if #game.save
format.html { redirect_to game_url(#game), notice: "Game was successfully created." }
format.json { render :show, status: :created, location: #game }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: #game.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /games/1 or /games/1.json
def update
respond_to do |format|
if #game.update(game_params)
format.html { redirect_to game_url(#game), notice: "Game was successfully updated." }
format.json { render :show, status: :ok, location: #game }
else
format.html { render :edit, status: :unprocessable_entity }
format.json { render json: #game.errors, status: :unprocessable_entity }
end
end
end
# DELETE /games/1 or /games/1.json
def destroy
#game.destroy
respond_to do |format|
format.html { redirect_to games_url, notice: "Game was successfully destroyed." }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_game
#game = Game.find(params[:id])
end
# Only allow a list of trusted parameters through.
def game_params
params.require(:game).permit(:code, :name)
end
end
In config/routes.rb you have defined resources :games, which creates default paths for CRUD actions. For the show action, which you are trying to get here, it would lead to /games/:id and the helper method would be game_path. You can also check this by running rails routes -c games command in the app directory. It should return all paths for games_controller
In the before_action callback for GamesController#show action, you are finding a Game object using Game.find(params[:id]). :id parameter is what you need to pass to the path helper that I mentioned earlier for the action to fire properly, so the path to a specific game would look like game_path(id: game.id). This will then automatically get converted to params. Alternatively, you can just pass the game object to the path helper and it will do the job for you like this: game_path(game)
Now in the WelcomeController#redirect action, you get the game code in params from the form submit. You need to first find the game for the submitted code like this:
game = Game.find_by(code: params[:param])
This should work if the code is unique for each game. Now that you have the correct game record, all you need is to redirect to the path that I've mentioned eariler:
redirect_to game_path(game)
I can't seem to find this anywhere - the console shows the field as nil but in reality action text is storing the content which could be 'blank'.
MyModel.rich_text_field.nil? returns false regardless if the actual content is blank or not.
You can check if your model field is blank with:
MyModel.rich_text_field.blank?
This is how I ended up handling validations for Action Text fields to determine if they were empty.
in my posts_controller I made sure to have if #post.save within the respond_to block.
# POST /posts or /posts.json
def create
#post = current_user.posts.new(post_params)
respond_to do |format|
if #post.save
flash[:success] = "Post was successfully created."
format.html { redirect_to #post }
format.json { render :show, status: :created, location: #post }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
in my Post model I added an attribute accessor with custom validations.
class Post < ApplicationRecord
attr_accessor :body
# Action Text, this attribute doesn't actually exist in the Post model
# it exists in the action_text_rich_texts table
has_rich_text :body
# custom validation (Note the singular validate, not the pluralized validations)
validate :post_body_cant_be_empty
# custom model validation to ensure the post body that Action Text uses is not empty
def post_body_cant_be_empty
if self.body.blank?
self.errors.add(:body, "can't be empty")
end
end
end
now the custom validation will be run to check if the Action Text post body is empty and if it is an error will be displayed to the user when the form is submitted.
I am trying to learn how to use namespaces in my Rails 5 app.
I have an organisation model and I have also made a series of nested models under the folder name "stance". One of those models is called overview.
The associations are:
Organisation.rb
has_one :overview, class_name: Stance::Overview
accepts_nested_attributes_for :overview, reject_if: :all_blank, allow_destroy: true
Stance::Overview
class Stance::Overview < ApplicationRecord
belongs_to :organisation, inverse_of: :overview
My controllers for stance resources are nested under a folder called stance.
My routes are:
namespace :stance do
resources :overviews
end
In my stance view partial, I am trying to render the attributes from the overview table.
I have tried:
<p><%= #overview.internal_explanation %></p>
<p><%= #stance_overview.internal_explanation %></p>
<p><%= #stance.overview.internal_explanation %></p>
<p><%= #stance::overview.internal_explanation %></p>
I want to display this partial in my organisation show. I am trying to do that with:
<%= render 'stance/overviews/internal', overview: #overview %>
But I can't figure out how to access the overview table. Do I need to add a reference to 'stance' in the associations?
I can see that in the console I need to write:
o = Stance::Overview.create(internal_explanation: "test")
o = Stance::Overview.first
but I can't see how to use that in the code itself.
I can see in the console that there is a record for this attribute.
The name of the table in the schema is "stance_overview".
My organisation controller has:
class OrganisationsController < ApplicationController
before_action :set_organisation, only: [:show, :edit, :update, :destroy]
def index
#organisations = Organisation.all
end
def show
end
def new
#organisation = Organisation.new
#organisation.build_overview
end
def edit
#organisation.build_overview unless #organisation.overview
end
def create
#organisation = Organisation.new(organisation_params)
respond_to do |format|
if #organisation.save
format.html { redirect_to #organisation, notice: 'Organisation was successfully created.' }
format.json { render :show, status: :created, location: #organisation }
else
format.html { render :new }
format.json { render json: #organisation.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #organisation.update(organisation_params)
format.html { redirect_to #organisation, notice: 'Organisation was successfully updated.' }
format.json { render :show, status: :ok, location: #organisation }
else
format.html { render :edit }
format.json { render json: #organisation.errors, status: :unprocessable_entity }
end
end
end
def destroy
#organisation.destroy
respond_to do |format|
format.html { redirect_to organisations_url, notice: 'Organisation was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_organisation
#organisation = Organisation.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def organisation_params
params.fetch(:organisation, {}).permit(:title, :comment,
overview_attributes: [:internal_explanation, :external_explanation ]
)
end
end
I have also tried defining the strong params for organisation as:
stance_overview_attributes: [:internal_explanation, :external_explanation ]
I keep getting an error that says:
undefined method `internal_explanation' for nil:NilClass
Can anyone refer me to materials to help me learn how to use namespaces in my app. I am trying to understand the fundamentals of this so that I can bank some knowledge. I am finding things through trial and error but not understanding what's actually required (although in this case, none of my attempts are working out).
To access Overview model(table) when you working not in Stance namespace you have to use Stance::Overview. If working for example in a controller that in Stance namespace you can use just Overview for access.
To get access from the relation you don't need any additional notation just #organisation.overview.
If I understand correctly in you case you have to declare your partial as
<%= render 'stance/overviews/internal', overview: #organisation.overview %>
and in the partial you have to use overview without #.
I understand that I cannot POST on an HTML redirect, but my situation requires that I redirect to create action after authenticating user. I would like to know how to bypass this restriction:
In particular, I would like to allow an user to fill out a post without logging in using Omniauth. I save the post to session[:post] using an AJAX call. Then, the user can login using omniauth and persist the post.
I have a PostsController with create action that handle initial ajax call, and also handle html request after authenticating user:
def create
#post = Post.new(params[:post])
respond_to do |format|
format.html{
if #post.save
redirect_to #post, notice: 'Post was successfully created.'
else
render action: "new"
end
}
format.json {
if session[:post] = #post
render json: #post, status: :created, location: #post
else
render json: #post.errors, status: :unprocessable_entity
end
}
end
end
end
Then, in my controller that handles callback from Facebook, I have:
class ServicesController < ApplicationController
def create
... authentication logic here ...
sign_in(:user, service.user)
redirect_to :controller => "posts", :action =>"create"
end
method_alias: :facebook, :create
However, this doesn't work, because I can't redirect to a "create" action. How can I accomplish this task?
In the code you posted, you never read the content of the session. I think it can work if you change your code with this :
Change initialization of #post:
#post = Post.new(params[:post]) || session[:post] # Find object in session if not
And add after post.save :
session.delete :post # clean session after successful creation
New full method:
def create
#post = Post.new(params[:post]) || session[:post] # Find object in session if not in params
respond_to do |format|
format.html{
if #post.save
redirect_to #post, notice: 'Post was successfully created.'
session.delete :post # clean session after successful creation
else
render action: "new"
end
}
format.json {
if session[:post] = #post
render json: #post, status: :created, location: #post
else
render json: #post.errors, status: :unprocessable_entity
end
}
end
end
end
You can create a method on a Post model and call it both from PostsController and ServicesController to save the post (though in this case it's quite trivial: new, then save, so you achieve nothing in terms of DRY, may be some encapsulation benefits). Or create a common mixin containing the create_post method with all the logic. Then and include it into SessionsController and PostsController and call it from 'create'.
In the mixin module:
def create_post(allow_json=false)
#post = Post.new(params[:post])
respond_to do |format|
format.html {
if #post.save
redirect_to #post, notice: 'Post was successfully created.'
else
render "posts/new"
end
}
if allow_json
... your post-saving & json-processing logic ...
end
end
end
In PostsController:
def create
create_post(true)
end
In SessionsController:
def create
... authentication logic here ...
sign_in(:user, service.user)
create_post(false)
end
I didn't compile and try, so I only hope it works. In general, I must say there's something basically wrong, so I'd look for other architectural solutions to achieve the same results, but as a quick-and-dirty approach it should probably work.
I found a hack to avoid this issue: I let #create handling AJAX call and write to session, then create another action to persist it into my database after user get authenticated:
class PostsController < ApplicationController
def create
#post = Post.new(params[:post])
respond_to do |format|
format.json {
if session[:post] = #post
render json: #post, status: :created, location: #post
else
render json: #post.errors, status: :unprocessable_entity
end
}
end
end
def persist
#post = session[:post]
if #post.save
session.delete :post
redirect_to #post, notice: 'Post was successfully created.'
else
render action: "new"
end
end
Then in my routes.rb, I have:
resources :posts do
collection do
get 'persist'
end
end
Finally, in my ServicesController:
sign_in(:user, service.user)
redirect_to persist_posts_path
I have a Ruby on Rails application where you can create 'posts'. I started of by using the scaffold generator to give generate the title which is a string and the body which is the content.
Each 'post' has a url of the id, for example /1, /2, /3, etc.
Is there a way to change that to a string of random characters, for example /49sl, /l9sl, etc?
Update
Here is what I have for the posts_controller.rb
class PostsController < ApplicationController
# GET /posts
# GET /posts.json
def index
#posts = Post.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: #posts }
end
end
# GET /posts/1
# GET /posts/1.json
def show
#post = Post.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #post }
end
end
# GET /posts/new
# GET /posts/new.json
def new
#post = Post.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #post }
end
end
# GET /posts/1/edit
def edit
#post = Post.find(params[:id])
end
# POST /posts
# POST /posts.json
def create
#post = Post.new(params[:post])
respond_to do |format|
if #post.save
format.html { redirect_to #post, notice: 'Post was successfully created.' }
format.json { render json: #post, status: :created, location: #post }
else
format.html { render action: "new" }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
# PUT /posts/1
# PUT /posts/1.json
def update
#post = Post.find(params[:id])
respond_to do |format|
if #post.update_attributes(params[:post])
format.html { redirect_to #post, notice: 'Post was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
# DELETE /posts/1
# DELETE /posts/1.json
def destroy
#post = Post.find(params[:id])
#post.destroy
respond_to do |format|
format.html { redirect_to posts_url }
format.json { head :no_content }
end
end
end
Rails uses the to_param method of an ActiveRecord object in order to resolve it into a URL.
Assuming you have a way to generate these unique ids (referring to it as IdGenerator) you can do the following:
1- Generate this id whenever you persist a Post object and save it to the database, let's say under the column url_id
class Post < ActiveRecord::Base
before_create :generate_url_id
def generate_url_id
self.url_id = IdGenerator.generate_id
end
end
2- Inside your Post model override the to_param method:
class Post < ActiveRecord::Base
def to_param
return url_id
end
end
Now post_path(#post) will resolve to /posts/url_id
By the way, you can use SecureRandom.urlsafe_base64 or look here if you don't have an ID generator yet.
Read more on the documentation for to_param.
I hope these two resources are going to help you :
The gem , named obfuscate_id . It represents the ID in a format like :
http://tennisfans.eu/products/4656446465
Another gem - masked_id . It provides a similar functionality . You are in control with a format of the url creation , defining it in a class . Looking at the source it appears , that this gem uses a strategy of obfuscate_id gem .
You can give your posts random URLs by following these 3 steps:
1- In your model (Post.rb), generate a random string for each post before it is saved. For example,
class Post < ActiveRecord::Base
before_create :generate_url_id
def generate_url_id
self.url_id = SecureRandom.urlsafe_base64
end
end
2- In your model (Post.rb), supply a to_param method to override Rails default URL generation. For example:
class Post < ActiveRecord::Base
def to_param
self.url_id
end
end
3- In your controller (PostsController.rb), use a dynamic finder to find your post by its random string. For instance,
class PostsController < ApplicationController
def show
#post = Post.find_by_url_id(params[:id])
...
end
end
I went ahead and put together a complete example and posted it to Github.
Next to Erez manual way you can use the friendly_id gem, with a unique id as your slug.
class Post < ActiveRecord::Base
# FriendlyId
friendly_id :uid
# Set a unique user id on create
before_save :set_uid, on: :create
def set_uid
self[uid] = rand(36**8).to_s(36)
end
end
Please note that the setting of the uid here does not ensure uniqueness. You certainly need to add some kind of validation, but that whole topic is a different one to google.
Friendly_id is a good solution, if you want to use a gem for it.
Follow this screencast:
http://railscasts.com/episodes/314-pretty-urls-with-friendlyid
(either video or asciicast, as you prefer)
Screencasts by Ryan Bates are really well done.
If you still want another option for id generation, you can try using UUIDs:
https://en.wikipedia.org/wiki/Universally_unique_identifier
And a ruby gem to generate them easily:
https://github.com/assaf/uuid
However, I would ask: Why do you want to make them random anyway? If what you are trying to do
is to avoid one of your users from typing another id in the url and accessing data that is not theirs, then probably you would want to limit access in the controller by scoping the finder, with something like this:
def show
#post = current_user.posts.where(:id => params[:id]).first
respond_to do |format|
format.html # show.html.erb
format.json { render json: #post }
end
end
In this case, current_user is a function that returns the current authenticated user (from session, or whatever you application logic dictates).