I'm trying to add save_with_initial_vote method to my Post class. It should both save the post and create its vote inside a transaction.
My post.rb looks like this:
class Post < ActiveRecord::Base
has_one :summary
has_many :comments, dependent: :destroy
has_many :votes, dependent: :destroy
belongs_to :user
belongs_to :topic
mount_uploader :image, ImageUploader
def up_votes
votes.where(value: 1).count
end
def down_votes
votes.where(value: -1).count
end
def points
votes.sum(:value)
end
def update_rank
age_in_days = (created_at - Time.new(1970,1,1)) / (60 * 60 * 24) # 1 day in seconds
new_rank = points + age_in_days
update_attribute(:rank, new_rank)
end
default_scope { order('rank DESC') }
scope :ordered_by_title, -> { order('posts.title ASC') }
scope :ordered_by_reverse_created_at, -> { order('posts.created_at DESC') }
validates :title, length: { minimum: 5 }, presence: true
validates :body, length: { minimum: 20 }, presence: true
validates :topic, presence: true
validates :user, presence: true
def markdown_title
render_as_markdown(self.title)
end
def markdown_body
render_as_markdown(self.body)
end
def create_vote
user.votes.create(value: 1, post: self)
end
def save_with_initial_vote
ActiveRecord::Base.transaction do
save
user.votes.create(value: 1, post: self)
end
end
private
def render_as_markdown(markdown)
renderer = Redcarpet::Render::HTML.new
extensions = {fenced_code_blocks: true}
redcarpet = Redcarpet::Markdown.new(renderer, extensions)
(redcarpet.render markdown).html_safe
end
end
My posts_controller.rb looks like this:
class PostsController < ApplicationController
def show
#topic = Topic.find(params[:topic_id])
authorize #topic
#post = Post.find(params[:id])
#comments = #post.comments
end
def new
#topic = Topic.find(params[:topic_id])
#post = Post.new
authorize #post
end
def edit
#topic = Topic.find(params[:topic_id])
#post = Post.find(params[:id])
authorize #post
end
def create
#topic = Topic.find(params[:topic_id])
#post = current_user.posts.build(post_params)
authorize #post
if #post.save_with_initial_vote
flash[:notice] = "Post was saved."
redirect_to [#topic, #post]
else
flash[:error] = "There was an error saving the post. Please try again."
render :new
end
end
def update
#topic = Topic.find(params[:topic_id])
#post = Post.find(params[:id])
authorize #post
if #post.update_attributes(post_params)
flash[:notice] = "Post was updated."
redirect_to [#topic, #post]
else
flash[:error] = "There was an error saving the post. Please try again."
render :new
end
end
def destroy
#topic = Topic.find(params[:topic_id])
#post = Post.find(params[:id])
authorize #post
if #post.destroy
flash[:notice] = "\"#{#post.title}\" was deleted."
redirect_to #topic
else
flash[:error] = "There was an error deleting your post. Please try again."
render :show
end
end
private
def post_params
params.require(:post).permit(:title, :body, :image)
end
end
My vote.rb looks like this:
class Vote < ActiveRecord::Base
belongs_to :user
belongs_to :post
validates :value, inclusion: {in: [-1, 1], message: "%{value} is not a valid vote." }
after_save :update_post
private
def update_post
post.update_rank
end
end
When I do, I get an error:
undefined method `-' for nil:NilClass
To troubleshoot your problem start by looking at the source for your error. The error says "undefined method '-' for nil:NilClass".
What this means is that you are trying to subtract from an object that is in fact Nil.
When you try to save a Vote, in vote.rb model you call update_post after save. This calls the update_rank method in the Post model. This method then subtracts a Time object from 'created_at'. The problem thus lies with created_at returning a nil value.
You can try using pry gem to debug your code better. You can then add 'binding.pry' anywhere in your model/controller code and step through each line to see what is the value of each variable at that instant.
Related
I'm getting a bit confused as to why I am receiving this error as (i think) it is suggesting that my current_customer returns nil. I do not know how to fix this, is it a problem with my current_customer method, sessions controller, or my events_controller...
Event Model:
class Event < ActiveRecord::Base
belongs_to :calendar
end
Events Controller:
class EventsController < ApplicationController
def new
#event = Event.new
#calendar = current_customer.calendar #WHY IS IT NOT ERRORING HERE TOO?
end
def create
**#calendar = current_customer.calendar #IT IS CALLING ERROR HERE**
#event = #calendar.build.event(event_params)
if #event.save
redirect_to '/main'
else
redirect_to '/compose'
end
end
private
def event_params
params.require(:event).permit(:calendar_id, :name, :starts_at, :ends_at)
end
Customer model:
class Customer < ActiveRecord::Base
belongs_to :business
has_one :calendar
has_secure_password
attr_accessor :remember_token
#remembers a user in the database for use in persistent sessions
def remember
self.remember_token = Customer.new_token
update_attribute(:remember_digest, Customer.digest(remember_token))
end
def Customer.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end
def forget
update_attribute(:remember_digest, nil)
end
def Customer.new_token
SecureRandom.urlsafe_base64
end
#returns true if the given token matches the digest
def authenticated?(remember_token)
BCrypt::Password.new(remember_digest).is_password?(remember_token)
end
end
Customer controller:
class CustomersController < ApplicationController
def new
#customer = Customer.new
#businesses = Business.all
#calendar = Calendar.new
end
def create
#customer = Customer.create(customer_params)
#calendar = #customer.build_calendar
#customer.save!
session[:customer_id] = #customer.id
redirect_to '/'
rescue ActiveRecord::RecordInvalid => ex
render action: 'new', alert: ex.message
end
private
def customer_params
params.require(:customer).permit(:first_name, :last_name, :business_no, :email, :password, :business_id)
end
end
Sessions controller:
class SessionsController < ApplicationController
def new
end
def create
#customer = Customer.find_by_email(params[:session][:email])
if #customer && #customer.authenticate(params[:session][:password])
session[:customer_id] = #customer.id #log in
#customer.remember
cookies.permanent.signed[:customer_id] = #customer.id
cookies.permanent[:remember_token] = #customer.remember_token
redirect_to '/main'
else
#need to add flash error: invalid password/email combination
redirect_to '/login'
end
end
def destroy
#current_customer.forget
cookies.delete(:customer_id)
cookies.delete(:remember_token)
session.delete(:customer_id)
#current_customer = nil
redirect_to '/'
end
end
Current_customer method within application.controller:
helper_method :current_customer
def current_customer
if (customer_id = session[:customer_id])
#current_customer ||= Customer.find_by(id: customer_id)
elsif (customer_id = cookies.signed[:customer_id])
customer = Customer.find_by(id: customer_id)
if customer && customer.authenticated?(cookies[:remember_token])
session[:customer_id] = customer.id #log in
#current_customer = customer
end
end
end
Calendar model:
class Calendar < ActiveRecord::Base
belongs_to :customer
has_many :events
end
calendars_controller:
class CalendarsController < ApplicationController
def new
#calendar = Calendar.new(calendar_params)
end
def create
#calendar = Calendar.new(calendar_params)
end
private
def calendar_params
params.require(:customer_id)
end
end
I am new to Ruby/Rails so I would really appreciate any explanations and answers, please help! thanks
#calendar = current_customer.calendar
#event = #calendar.build.event(event_params)
According to the error message, #calendar is nil, so when you call #calendar.build.event(event_params) in the next line, it calls: nil.build and hence you get the error.
For some reason, your current_customer's calendar is nil for that request. Figure that out and make sure the current_customer has a calendar present in the database, then it should work.
Also, you need to change:
#event = #calendar.build.event(event_params)
To:
#event = #calendar.events.build(event_params)
It would seem that #calendar is nil in the events controller.
Start by checking the current_customer passes back the customer you were expecting.
Then check that the current_customer has a calendar created for you to reference.
I'm having some trouble with Nokogiri.
I'm new to this xml parsing and I seem to get no errors. I get the order imported alert.
Which means it may be permitted perimeters. If I'm right then, Could I create a model that allows all permitted perimeters. So please take a look, thank you for your time, here is my code:
Routes.rb
resources :orders do
collection { post :import }
get "/confirm" => "confirmations#show"
get 'dconfirmation' => 'orders#confirmation'
end
Model
class Order < ActiveRecord::Base
belongs_to :user
scope :not_completed_orders, -> {
where(:complete => false)
}
require 'nokogiri'
def self.import(file)
doc = Nokogiri::XML.parse(file)
#your processing code goes here
end
end
Controller Order
class OrdersController < ApplicationController
before_filter :authenticate_user!
def new
#order = Order.new
end
def create
#order = current_user.orders.new(order_params)
#order.email = current_user.email
#order.name = current_user.name
#order.address_line_1 = current_user.address_line_1
#order.address_line_2 = current_user.address_line_2
#order.postcode = current_user.postcode
#order.city = current_user.city
#order.country = current_user.country
if #order.save
redirect_to dconfirmation_path
end
end
def order_params
params.require(:order).
permit(
:email,
:delivery_name,
:company_name,
:delivery_address1,
:delivery_address2,
:delivery_address3,
:delivery_city,
:delivery_postcode,
:delivery_country,
:phone,
:package_contents,
:description_content,
:restricted_items,
:terms_conditions,
:insurance,
:contents_value,
:cf_reference,
:reference_number,
:service
)
end
def show
#user = User.find(params[:id])
end
def confirmation
end
def import
Order.import(params[:file])
redirect_to root_url, notice: "Order imported."
end
end
View
= form_tag import_orders_path, multipart: true do
= file_field_tag :file
= submit_tag "Import"
I am having some trouble with my new public/private topic methods. I need to be able to apply a publicly_viewable method to my topics scope. However, I keep getting the following error:
SyntaxError in TopicsController#index
/Users/ericpark/rails_projects/bloccit-2/app/models/topic.rb:7: syntax error, unexpected '(', expecting '}' scope :visible_to, -> { :publicly_viewable(user) } ^ /Users/ericpark/rails_projects/bloccit-2/app/models/topic.rb:20: syntax error, unexpected keyword_end, expecting '}'
Extracted source (around line #7):
5
6
7
8
9
10
validates :name, length: {minimum: 5}, presence: true
scope :visible_to, -> { :publicly_viewable(user) }
def publicly_viewable(user)
if user
I have also defined the publicly_viewable method within my topic model:
class Topic < ActiveRecord::Base
has_many :posts, dependent: :destroy
belongs_to :user
validates :name, length: {minimum: 5}, presence: true
scope :visible_to, -> { :publicly_viewable(user) }
def publicly_viewable(user)
if user
topic_collection.all
else
topic_collection.where(public: true)
end
end
def privately_viewable
topic_collection.where(public: false)
end
end
Topics Controller:
class TopicsController < ApplicationController
def index
#topics = Topic.visible_to(current_user).paginate(page: params[:page], per_page: 10)
authorize #topics
end
def show
#topic = Topic.find(params[:id])
#posts = #topic.posts.paginate(page:params[:page])
authorize #topic
end
def new
#topic = Topic.new
authorize #topic
end
def edit
#topic = Topic.find(params[:id])
authorize #topic
end
def create
#topic = Topic.new(topic_params)
authorize #topic
if #topic.save
redirect_to #topic, notice: "Topic was saved successfully."
else
flash[:error] = "Error creating topic. Please try again."
render :new
end
end
def update
#topic = Topic.find(params[:id])
authorize #topic
if #topic.update_attributes(topic_params)
redirect_to #topic
else
flash[:error] = "Error saving topic. Please try again."
render :edit
end
end
def destroy
#topic = Topic.find(params[:id])
authorize #topic
if #topic.destroy
flash[:notice] = "\"#{#topic.name}\" was deleted successfully."
redirect_to topics_path
else
flash[:error] = "There was an error deleting the topic."
render :show
end
end
private
def topic_params
params.require(:topic).permit(:name, :description, :public)
end
end
I am still quite new to scopes so anytime will be greatly appreciated.
NameError in Comments#create
Showing /comments/_form.html.erb
where line #1 raised:
undefined local variable or method new_comment for #<#:0x007fb9760eb640>
<%= form_for new_comment, url: send(create_url, new_comment.commentable) do |f| %>
new_comment is being called by:
activities/index.html.erb
<%= render "comments/form", new_comment: Comment.new(commentable_id: activity.id, commentable_type: activity.class.model_name), create_url: :activity_comments_path %>
comment.rb
class Comment < ActiveRecord::Base
after_save :create_notification #ERROR OCCURRED WHEN I INTRODUCED THIS LINE AND
validates :activity, presence: true #THIS LINE
validates :user, presence: true
has_many :notifications
belongs_to :commentable, polymorphic: true
belongs_to :user
belongs_to :activity
private
def create_notification
Notification.create(
activity_id: self.activity_id,
user_id: self.user_id,
comment_id: self.id,
read: false
)
end
end
notification.rb
class Notification < ActiveRecord::Base
belongs_to :activity
belongs_to :comment
belongs_to :user
end
activity.rb
class Activity < ActiveRecord::Base
self.per_page = 20
has_many :notifications
belongs_to :user
has_many :comments, as: :commentable
belongs_to :trackable, polymorphic: true
def conceal
trackable.conceal
end
def page_number
(index / per_page.to_f).ceil
end
private
def index
Activity.order(created_at: :desc).index self
end
end
routes.rb
resources :activities do
resources :comments
resources :notifications
member do
post :like
post :notifications
end
end
Any ideas on what maybe the cause? This error is coming off of the awesome answer here: How to make a path to a paginated url?
Thank you!
UPDATE
class CommentsController < ApplicationController
before_action :load_commentable
before_action :set_comment, only: [:show, :edit, :update, :destroy, :like]
before_action :logged_in_user, only: [:create, :destroy]
def index
#comments = #commentable.comments
end
def new
#comment = #commentable.comments.new
end
def create
#comment = #commentable.comments.new(comment_params)
if #comment.save
redirect_to #commentable, notice: "comment created."
else
render :new
end
end
def edit
#comment = current_user.comments.find(params[:id])
end
def update
#comment = current_user.comments.find(params[:id])
if #comment.update_attributes(comment_params)
redirect_to #commentable, notice: "Comment was updated."
else
render :edit
end
end
def destroy
#comment = current_user.comments.find(params[:id])
#comment.destroy
redirect_to #commentable, notice: "comment destroyed."
end
def like
#comment = Comment.find(params[:id])
#comment_like = current_user.comment_likes.build(comment: #comment)
if #comment_like.save
#comment.increment!(:likes)
flash[:success] = 'Thanks for liking!'
else
flash[:error] = 'Two many likes'
end
redirect_to(:back)
end
private
def set_comment
#comment = Comment.find(params[:id])
end
def load_commentable
resource, id = request.path.split('/')[1, 2]
#commentable = resource.singularize.classify.constantize.find(id)
end
def comment_params
params[:comment][:user_id] = current_user.id
params.require(:comment).permit(:content, :commentable, :user_id, :like)
end
end
notifications_controller
class NotificationsController < ApplicationController
def index
#notifications = current_user.notifications
#notifications.each do |notification|
notification.update_attribute(:read, true)
end
end
def destroy
#notification = Notification.find(params[:id])
#notification.destroy
redirect_to :back
end
end
activities_controller
class ActivitiesController < ApplicationController
def index
#activities = Activity.order("created_at desc").paginate(:page => params[:page])
end
def show
redirect_to(:back)
end
def like
#activity = Activity.find(params[:id])
#activity_like = current_user.activity_likes.build(activity: #activity)
if #activity_like.save
#activity.increment!(:likes)
flash[:success] = 'Thanks for liking!'
else
flash[:error] = 'Two many likes'
end
redirect_to(:back)
end
end
It should be <%= form_for comment do |f| %> not new comment since comment is the element in your database
Morning All,
After spending most of the night figuring out how to put a limit on my model creation I finally got somewhere. The nested statement is now presenting me with not saved which is great news.
However I cannot seem to get the redirect or flash[:base] to work. Here is the code below:
class SnippetsController < ApplicationController
before_filter :find_book
def create
if #snippet = #book.snippets.create!(params[:snippet])
redirect_to #book
else
flash[:base]
#render
end
end
def approve
##snippet = #book.snippet.find(params[:id])
if #snippet.update_attribute(:approved, true)
redirect_to users_path
else
render root_path
end
end
def edit
#snippet = #book.snippets.find(params[:id])
end
def update
#snippet = #book.snippets.find(params[:id])
respond_to do |format|
if #snippet.update_attributes(params[:snippet])
format.html { redirect_to #book, notice: 'Comment was successfully updated.' }
else
format.html { render action: "edit" }
end
end
end
private
def find_book
#book = Book.find(params[:book_id])
end
end
Models parent (book)
class Book < ActiveRecord::Base
has_many :snippets
attr_accessible :title, :book_id, :size
def snippets_limit_reached?
if size == 0
self.snippets.count >= 2
elsif size == 1
self.snippets.count >= 3
elsif size == 2
self.snippets.count >= 4
else
return false
end
end
end
Child (Snippet)
class Snippet < ActiveRecord::Base
before_create :check_limit
belongs_to :book
attr_accessible :content, :book_id
validates :book_id, presence: true
def check_limit
if book.snippets_limit_reached?
errors.add :base, 'Snippet limit reached.'
return false
end
return true
end
end
Let me know if you need anything else, just fyi when it's running I cannot get past the nested create!
if #snippet = #book.snippets.create!(params[:snippet])
Bang methods (create!, save!) throw errors when unsuccessful, instead of returning, what evaluates to false.
Removing the bang should fix this problem.