I'm having trouble getting an integration test to pass. A user creates a product, which can have many pictures. The product's new.html.erb contains file field <%= file_field_tag "images[]", type: :file, multiple: true %> The code works fine, I'm just having problems creating a working integration test.
products_creation_test.rb:
require 'test_helper'
class ProductsCreationTest < ActionDispatch::IntegrationTest
.
.
.
test "should create new product with valid info" do
log_in_as(#user)
pic1 = fixture_file_upload('test/fixtures/cat1.jpg', 'image/jpg')
pic2 = fixture_file_upload('test/fixtures/profile.png', 'image/png')
assert_difference 'Product.count', 1 do
post user_products_path(#user), product: { title: 'test',
description: 'test',
price: '5.99',
images: [pic1, pic2] }
end
assert_redirected_to #user
#user.reload
newprod = #user.products.last
pics = newprod.pictures.all
assert_equal 2, pics.count
end
end
This fails the last assertion, stating that there are no pictures associated with the new product, when there should be 2. inspecting the pics variable, i get the following error: RuntimeError: #<ActiveRecord::AssociationRelation []
What am I missing?
my model structure is as follows:
User has_many Products, Products has_many Pictures
products_controller.rb:
class ProductsController < ApplicationController
before_action :valid_user, only: [:new, :create, :edit, :update]
.
.
.
def create
#product = current_user.products.build(product_params)
if #product.save
# to handle multiple images upload on create
if params[:images]
params[:images].each { |image|
#product.pictures.create(image: image)
}
end
flash[:success] = "Product Created!"
redirect_to current_user
else
flash[:alert] = "Something went wrong."
render :new
end
end
.
.
.
end
pictures_controller.rb:
class PicturesController < ApplicationController
def create
#picture = Picture.new(picture_params)
#picture.save
end
def destroy
end
private
def picture_params
params.require(:picture).permit(:product_id, :image, :_destroy)
end
end
picture.rb:
class Picture < ActiveRecord::Base
belongs_to :product
mount_uploader :image, PicturesUploader
validates_integrity_of :image
validates_processing_of :image
validates :image, file_size: { less_than: 10.megabytes }
end
Solved as follows:
revised products_creation_test.rb:
require 'test_helper'
class ProductsCreationTest < ActionDispatch::IntegrationTest
.
.
.
test "should create new product with valid info" do
log_in_as(#user)
pic1 = fixture_file_upload('test/fixtures/cat1.jpg', 'image/jpg')
pic2 = fixture_file_upload('test/fixtures/profile.png', 'image/png')
assert_difference 'Product.count', 1 do
post user_products_path(#user), product: { title: 'test',
description: 'test',
price: '5.99',
}, images: [pic1, pic2]
end
assert_redirected_to #user
newprod = assigns(:product)
assert_equal 2, newprod.pictures.count
end
end
Related
I am trying to implement multi-select in a form in rails 4, using Semantic UI. I want users to be able to select multiple categories for each post. So far, I am able to display the dropdown select field which pulls all the categories from the database as follows:
<%= form_for #post, :html => {:class => "ui form bk-form group"} do |f| %>
<label>Post title:</label><br />
<%= f.text_field :title, autofocus: true %>
<label>Choose a category:</label><br />
<%= f.select(:category_ids,options_for_select(Category.all.collect{|x| [x.name,x.id,class:'item']}),{prompt:'Select categories'}, multiple: true, class:'ui fluid dropdown selection multiple')%>
<% end %>
With this, I am able to create and save posts and the data is inserted in the database. However, when I try to edit an article, the pre-selected categories do not show. I have tried to set the value: #post.categories option in the select field and still cannot get the existing categories to show. Thanks in advance for your thoughts.
UPDATED
Models are as follows:
class Post < ActiveRecord::Base
has_many :post_categories
has_many :categories, through: :post_categories
end
class Category < ActiveRecord::Base
has_many :post_categories
has_many :posts, through: :post_categories
end
class PostCategory < ActiveRecord::Base
belongs_to :post
belongs_to :category, :counter_cache => :posts_count
end
Then my posts_controller.rb
class PostsController < ApplicationController
before_action :set_post, only: [:edit, :update, :show]
def index
#posts = Post.all
end
def new
end
def create
#post = Post.new(post_params)
#post.user = current_user
if #post.save
flash[:notice] = "Post was successfully created"
redirect_to user_posts_path
else
flash[:alert] = "Oh Snap!! We could not save your post"
render 'new'
end
end
def edit
end
def update
if #post.update(post_params)
flash[:notice] = "Post was successfully updated"
redirect_to user_posts_path
else
flash[:alert] = "Oh Snap!! We could not update your post"
render 'edit'
end
end
private
def post_params
params.require(:post).permit(:title, :description, :published, :tag_list, category_ids: [])
end
def set_post
#post = Post.find(params[:id])
end
end
Hi I have a rails App where Users can have a timetable with a one to many relationship.
When i add this
<%= link_to "delete", user, method: :delete,
data: { confirm: "You sure?" } %>
to _user.html it renders a delete link which deletes a user no problem
but when i add this
<%= link_to "delete", timetable, method: :delete,
data: { confirm: "You sure?" } %>
to _timetable.html.erb it throws an error
ActionController::RoutingError (No route matches [DELETE] "/timetable.4"):
My Routes.rb
get 'password_resets/new'
get 'password_resets/edit'
root 'static_pages#home'
get 'help' => 'static_pages#help'
get 'about' => 'static_pages#about'
get 'contact' => 'static_pages#contact'
get 'timetable' => 'timetables#new'
get 'signup' => 'users#new'
get 'mobile' => 'users#mobile'
get 'login' => 'sessions#new'
post 'login' => 'sessions#create'
delete 'logout' => 'sessions#destroy'
resources :account_activations, only: [:edit]
resources :password_resets, only: [:new, :create, :edit, :update]
resources :timetables
resources :users
resources :projects
Timetable Controller
class TimetablesController < ApplicationController
before_action :logged_in_user, only: [:create, :destroy]
before_action :correct_user, only: :destroy
def index
#timetables = current_user.timetables.find(current_user)
end
def new
#user = User.find(current_user)
#timetable = Timetable.new
#timetables = #user.timetables.paginate(page: params[:page], per_page: 1)
end
def feed
Timetable.where("user_id = ?", id)
end
def show
#feed_items3 = current_user.feed.paginate(page: params[:page])
#timetable = current_user.timetables.find(params[:id])
end
def create
#timetable = Timetable.new(timetable_params)
#user = User.find(current_user)
if current_user.timetables.create(timetable_params)
flash[:success] = "Timetable created!"
redirect_to timetable_path
else
flash[:success] = "Timetable not created!"
redirect_to timetable_path
end
end
def destroy
#timetable = Timetable.find(params[:id])
#timetable.destroy
redirect_to timetables_path, notice: "The timetable #{#timetable.name} has been deleted."
end
private
def timetable_params
params.require(:timetable).permit(:name, :attachment, :id)
end
def correct_user
#project = current_user.projects.find_by(id: params[:id])
redirect_to root_url if #project.nil?
end
end
Without seeing your Controllers it is hard to be sure, but I would look at a few areas:
Specifying the path & the timetable object to be deleted:
<%= link_to "Delete Timetable", timetable_path(timetable), :method => :delete %>
Ensure that the destroy action for the timetable sits within the timetable_controller
Regarding your routes.rb file I am not sure why you need:
get 'timetable' => 'timetables#new'
and then also
resources :timetables
This is duplication - why not try use 'only' (edit to your preference):
resources :timetables, only: [:index, :new, :create, :destroy]
I want to add comment in post in simply ruby on rails application.
Resolved.
My Controller:
class CommentsController < ApplicationController
before_filter :authenticate_user!
def create
binding.pry
#adventure = Adventure.find(params[:adventure_id])
#comment = #adventure.comments.create(comment_params)
if #comment.save
flash[:notice] = "Comment has been created."
redirect_to #adventure
else
flash.now[:danger] = "error"
end
end
def comment_params
params.require(:comment).permit(:body)
end
end
class Comment < ActiveRecord::Base
belongs_to :adventure
end
Total Rails novice here, just coming from PHP.
I'm running a Rails4+Active Admin setup with (device/cancan (disregarding those now).
Implementing strong_parameters. Please note, the code is totally broken. I've tried it in a bunch of ways and this is as close as I've gotten. Hopefully it gives a view into what I'm trying to do.
Been following a tutorial on implementing an API key setup, and trying to modify it to be able to generate an APIKey for a user in the admin.
I've added a column to AdminUser to generate an APIKey
It is correctly linked to
#app/admin/admin_users.rb
ActiveAdmin.register AdminUser do
index do
column :email
column :current_sign_in_at
column :last_sign_in_at
column :sign_in_count
default_actions
column('API', :sortable => :id) { |resource| link_to "(Re)Generate Key", new_admin_admin_user_api_key_path(resource.id) }
end
end
Which gives me a (correct) link to
/admin/admin_users/:admin_user_id/api_keys/new(.:format)
# app/admin/api_key.rb
ActiveAdmin.register APIKey do
belongs_to :admin_user
controller do
def create_for admin_user
key = APIKey.new
key.assign_params_from_controller(admin_user)
APIKey.create(:admin_user_id => params[:admin_user_id]);
end
def permitted_params
params.permit create_for [:admin_user_id]
end
end
end
#app/models/api_key.rb
class APIKey < ActiveRecord::Base
before_create :generate_access_token
belongs_to :admin_user, :foreign_key => "id", :inverse_of => :api_key
attr_accessible :admin_user_id
def assign_params_from_controller(params)
#params = params
end
private
def generate_access_token
begin
self.admin_user_id = params.admin_user_id
self.access_token = SecureRandom.hex
end while self.class.exists?(admin_user_id: admin_user_id, acces_token: access_token)
end
end
This code gives me:
NameError in Admin::APIKeysController#new
undefined local variable or method `params' for #<APIKey:0x000000078d6470>
def generate_access_token
begin
self.admin_user_id = params.admin_user_id
self.access_token = SecureRandom.hex
end while self.class.exists?(admin_user_id: admin_user_id, acces_token: access_token)
end
UPDATE, ANSWER:
Problem solved.
Ended up being on the right track. But was straying off because of type conversion errors.
My admin_user_id ended up being a 'string' in the database. Nice copy-paste job there.
Wrote a migration first
class AlterTableAPIKeys < ActiveRecord::Migration
def up
execute "DELETE FROM `api_keys` WHERE 1"
change_column :api_keys, :access_token, :string, { null: false }
change_column :api_keys, :admin_user_id, :integer, { null: false }
add_column :api_keys, :active, :boolean, {null: false, default: true }
remove_column :api_keys, :role
add_index :api_keys, ["admin_user_id"], name: "index_api_keys_on_admin_user_id", unique: false
add_index :api_keys, ["access_token"], name: "index_api_keys_on_access_token", unique: true
end
end
I didn't pass the access_token to the create.
I ended up with this.
# app/admin/api_key.rb
ActiveAdmin.register APIKey do
belongs_to :admin_user
controller do
def new
key = APIKey.create(:admin_user_id => params[:admin_user_id])
{:access_token => key.access_token}
redirect_to admin_admin_users_path, :notice => "API Key #{key.access_token} created! "
end
def permitted_params
params.permit api_key: [:admin_user_id]
end
end
end
# app/models/api_key.rb
class APIKey < ActiveRecord::Base
attr_accessible :access_token, :expires_at, :admin_user_id, :active, :application
before_create :generate_access_token
before_create :set_expiration
belongs_to :admin_user
def expired?
DateTime.now >= self.expires_at
end
private
def generate_access_token
begin
self.access_token = SecureRandom.hex
end while self.class.exists?(access_token: access_token)
end
def set_expiration
self.expires_at = DateTime.now+30
end
end
Obviously this does not account for access, might solve that with roles (i.e. uberadmin can regenerate other admins API, admins only their own).
My model:
class Product < ActiveRecord::Base
has_many :product_images, dependent: :destroy
accepts_nested_attributes_for :product_images, :reject_if => lambda { |p| p['image'].blank? }, :allow_destroy => true
end
class ProductImage < ActiveRecord::Base
belongs_to :product
mount_uploader :image, ProductImageUploader
validates_presence_of :image
end
My controller:
def create
#product = Product.new(permitted_params.product)
if #product.save
redirect_to edit_admin_product_path(#product), notice: "success"
else
render :new
end
end
def update
#product = Product.find(params[:id])
if #product.update_attributes(permitted_params.product)
redirect_to edit_admin_product_path(#product), notice: "success"
else
render :edit
end
end
permitted_params:
class PermittedParams < Struct.new(:params)
def product
params.require(:product).permit(*product_attrs)
end
def product_attrs
[:name, :content, :stock, :list_price, :selling_price, :bonus, :is_added,
product_images_attributes: [:id, :image, :_destroy] ]
end
end
And parameters passed:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"eqweq1231sda+3T0131643guXybT75X6NqN1ng=", "product"=>{"name"=>"weqwe", "content"=>"qweq", "product_images_attributes"=>{"0"=>{"image"=>"1069218_513152615405563_1187314087_n.jpg", "_destroy"=>""}}, "stock"=>"", "list_price"=>"", "selling_price"=>"123", "bonus"=>""}, "commit"=>"submit"}
Obviously the image is pass to the params. but when create product it will rollback to alarm the image is empty(validate presence image in my ProductImage model).
If I delete the validation then create the product. I can successfully upload the images in update action. I have totally no idea what's the problem! Please help. Q_Q