Create and view by has_many :through association rails - ruby-on-rails

I am using rails4 and also new to ROR.
I am using devise to create User. There is "workspace" model which is related to user though "user_workspace" model.
user.rb
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :user_workspaces
has_many :workspaces, through: :user_workspaces
end
workspace.rb
class Workspace < ActiveRecord::Base
has_many :users, through: :user_workspaces
has_many :user_workspaces
end
user_workspace.rb
class UserWorkspace < ActiveRecord::Base
belongs_to :user
belongs_to :workspace
end
I want to create a workspace and show it in the index page
workspace_controller
def create
#user=User.find(params[:id])
#user_worspace = #user.user_workspaces.build(params[:user_worspace])
#workspace = #user_worspace.build_workspace(params[:workspace].permit(:name))
if #workspace.save
flash[:notice] = "workspace created"
redirect_to workspaces_path
else
flash[:error] = "Name cant be blank"
redirect_to workspaces_path
end
end
def index
#user=User.find(params[:id])
#user_worspace = #user.user_workspaces.build(params[:user_worspace])
#workspace = #user_worspace.build_workspace
#users = current_user.user_workspaces.collect(&:workspace_id)
#workspaces =#users.workspaces.all
end
I have given new in my index as I am using modal view to create new workspace form the index page.
index.html.erb
<div class = "container">
<% if #users.present? %>
<% #workspaces.each do |workspace| %>
<div class= "workspace-list">
<strong><%= link_to workspace.name, workspaces_show_path(workspace.id) %></strong> </div>
<% end %>
<% end %>
</div>
I would like to know the exact way of creating and displaying the things using has_many :throught association.

Related

How to retrieve a value of a "belongs to" element attribute on an index page in rails?

I try to make a web application where I have 4 models :
User, Recipe, Step, Steps_advancement.
Globally, a user can like a recipe composed by steps. When the user opens and reads a step of the recipe, it puts a "is_read=true" on the table steps_advancement which belongs to user and step.
For the moment I have this code :
Models :
class Recipe < ApplicationRecord
belongs_to :user
has_many :steps
has_many :favorites
has_many :favorited_by_users, :through => :favorites, :source => 'user'
class Step < ApplicationRecord
belongs_to :recipe
has_one :steps_advancement
end
class StepsAdvancement < ApplicationRecord
belongs_to :step
belongs_to :user
end
class Favorite < ApplicationRecord
belongs_to :recipe
belongs_to :user
end
class User < ApplicationRecord
has_many :recipes
has_many :favorites
has_many :favorite_recipes, :through => :favorites, :source => 'recipe’
has_many :steps_advancements
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
end
In the database, i have a table Steps_advancement with a boolean "is_read", default: false, null: false (and recipe_id and step_id on index)
In the views, the steps index page lists all the steps of the recipe.
I want to be able to retrieve, in the steps index view, the value of the steps_advancement ‘is_read’. In order to display it if the previous is read, or hide it, else.
But I can't, I have an error message.
StepsController
def index
#recipe = Recipe.find(params[:recipe_id])
#steps = #recipe.steps
in the steps index view (index.html.erb)
<% if current_user %>
<% #steps.each do |st|%>
<% if st.steps_advancement.is_read?%> # <<< error here
<%= link_to([#recipe, steps]) do %>
<strong><%=st.name %></strong> --
<%else%>
<%="not avalaible for the moment!"%>
<% end %>
<% end %>
In the third line I have a problem. I cannot access the steps_advancement. I guess rails does not recognize the step but how can i resolve it ? :)
In advance thanks!
So step_advancement is not created on step until it is read, correct?
Add a try before is_read? like such:
steps/index.html.erb
<% if current_user %>
<% #steps.each do |st|%>
<% if st.steps_advancement.try(:is_read?)%> # <<< error here
<%= link_to([#recipe, steps]) do %>
use this:
seems your code is fine here i'm skipping <% if st.steps_advancement.is_read?%> block if st.steps_advancement is blank.
<% if current_user %>
<% #steps.each do |st|%>
<%next if st.steps_advancement.blank?%>
<% if st.steps_advancement.is_read?%> # <<< error here
<%= link_to([#recipe, steps]) do %>
<strong><%=st.name %></strong> --
<%else%>
<%="not avalaible for the moment!"%>
<% end %>
<%end%>
<% end %>
I don't know if it will work, but you can try to add nested attributes for
class Step < ApplicationRecord
belongs_to :recipe
has_one :steps_advancement
accepts_nested_attributes_for :steps_advancement
def steps_advancement
self.steps_advancement
end
end
EDIT
I don't know if could work but maybe that could be a workaround solution

Rails 5 generate parent record on child creation, only if not already present

I have two models, User and Company. I have used the device gem for User model creation.
class User < ApplicationRecord
belongs_to :company
accepts_nested_attribute_for :company
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :confirmable, :lockable
end
class Company < ApplicationRecord
has_many :users
end
When I create users, I want to associate with them the company they work in. I have included the company_name attribute in the user creation form. What I don't want is the company table to have multiple records for the same company_name attribute.
<% form_for(#user) do |user_form| %>
<% user_form.fields_for :company do |company_form| %>
<%= company_form.label :company_name %>
<%= company_form.text_field :company_name %>
<% end %>
# other fields for user
<% end %>
I want to check if the company the user is associated with, is already present or not in the company table. Create a new record for the company only if it is not already present.
class User < ApplicationRecord
before_save :create_or_initialize_company
private
def create_or_initialize_company
Company.where("name ILIKE '%#{company_name}%'").first_or_create
end
end
Now here, you can do couple of variations based on your requirements e.g.
If you want exact match then .where(name: company_name)
If you don't want case insensitive match then replace ILIKE with LIKE.
Hope it helps..
class UsersController < ApplicationController
def create
#user = User.new(user_params)
assign_new_or_existing_company(#user)
if #user.save
# ...
end
def update
#user = User.find(params[:id])
#user.assign_attributes(user_params)
assign_new_or_existing_company(#user)
if #user.save
# ...
end
private
def assign_new_or_existing_company(user)
user.company = Company.where(
'company_name ILIKE ?',
"%#{user_params[:company_attributes][:company_name]}%"
)
.first_or_initialize(user_params[:company_attributes])
end
def user_params
params.require(:user).permit(:id, company_attributes: [:company_name])
end
end

How to add existing songs to a music library Rails 4

What I'm trying to do is add songs that artists have already uploaded to a user library (I have already set up my app so that artists can upload songs). Also, I have set up my code so that an empty user library is created after a user signs up (using the after_create Active Record Callback).
To be more clear, I would like for the user to be able to add songs they see within the site to their library.
However, this is escaping me. I am familiar with CRUD, and have an idea how I would create a library and add existing songs to it, but I am not quite sure how I could add a song to a user library by clicking a button/link saying "Add Song To Library" which would be next to a song, and having it add to the user's existing empty library.
My existing code is below.
User.rb:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
belongs_to :meta, polymorphic: true
before_create :create_empty_profile
after_create :create_empty_library #may not be the best way to do it ¯\_(ツ)_/¯
acts_as_messageable
has_many :playlists
has_many :user_friendships, dependent: :destroy
has_many :friends, -> { where(user_friendships: { state: 'accepted'}) }, through: :user_friendships
has_many :pending_user_friendships, -> { where ({ state: 'pending' }) }, class_name: 'UserFriendship', foreign_key: :user_id
has_many :pending_friends, through: :pending_user_friendships, source: :friend
has_many :chat_rooms, dependent: :destroy
has_many :chat_messages, dependent: :destroy
has_many :votes, dependent: :destroy
mount_uploader :profile_pic, ProfilePicUploader
def mailboxer_name
self.name
end
def mailboxer_email(object)
self.email
end
def admin?
role == 'admin'
end
def moderator?
role == 'moderator'
end
def create_empty_profile
if is_artist?
profile = ArtistProfile.new
else
profile = UserProfile.new
end
profile.save(validate: false)
self.meta_id = profile.id
self.meta_type = profile.class.name
end
def create_empty_library
library = Library.new
library.user_id = self.id
library.save(validate: false)
end
end
Library.rb:
class Library < ActiveRecord::Base
belongs_to :user
has_many :library_songs
has_many :songs, through: :library_songs
has_many :library_albums
has_many :albums, through: :library_albums
end
library_song.rb
class LibrarySong < ActiveRecord::Base
belongs_to :library
belongs_to :song
end
library_album.rb
class LibraryAlbum < ActiveRecord::Base
belongs_to :library
belongs_to :album
end
libraries_controller.rb
class LibrariesController < ApplicationController
def index
#libraries = Library.all
end
def show
#library = Library.find(params[:id])
end
end
I was able to create playlists and add songs to them using the form/controller below.
playlists/new.html.erb:
<h1>New Playlist</h1>
<%= form_for(#playlist) do |f| %>
<%= f.text_field :name %>
<% Song.all.each do |song| -%>
<div>
<%= check_box_tag :song_ids, song.id, false, :name => 'playlist[song_ids][]', id: "song-#{song.id}" %>
<%= song.name %>
</div>
<% end %>
<%= f.submit %>
<% end %>
playlists_controller.rb:
class PlaylistsController < ApplicationController
def index
#playlists = Playlist.all
end
def show
#playlist = Playlist.find(params[:id])
end
def new
#playlist = Playlist.new
end
def create
#playlist = Playlist.create(playlist_params)
redirect_to #playlist
end
private
def playlist_params
params.require(:playlist).permit(:name, song_ids: [])
end
end
However, the main issue is that in the form above, the playlist is being created along with the existing songs. In this case, I would need to add existing songs to an existing library that is empty.
Any ideas, guys? This would be very helpful. I would be happy to upload any code needed.
It looks to me like you don't actually have has_many :libraries set in your user model. Judging by your Library model, I think this was what you had intended. You should really just create the 'new' models before you save the User model. You could use something similar to this and do it all in one action.
class User < ActiveRecord::Base
def self.build_full_user(params, songs)
# Assign all normal attributes here
new_user = User.new
new_user.name = params[:name]
# If you want to assign new songs, just make a new Library model and associate them.
new_library = Library.new
# Build the song models if you haven't found/created or passed them in already.
new_songs = Songs.build_songs_from_list(songs)
new_library.songs << new_songs
new_user.libraries << new_library
# You can do the save check here or up one level if you'd like.
return new_user
end
end

Rails Client side Collection Validation fails - Simple Form

I have to build a simple app that allows users to loan and borrow books. Simply put a User can create books, and they can pick another user to loan the book to.
I have three models User, Book and Loan:
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :books
has_many :loans, through: :books
has_many :borrowings, class_name: "Loan"
validates :username, uniqueness: true
validates :username, presence: true
end
class Book < ActiveRecord::Base
belongs_to :user
has_many :loans
validates :title, :author, presence: true
end
class Loan < ActiveRecord::Base
belongs_to :user
belongs_to :book
validates :user, :book, :status, presence: true
end
The LoansController looks like this:
class LoansController < ApplicationController
before_action :find_book, only: [:new, :create]
def new
#users = User.all
#loan = Loan.new
authorize #loan
end
def create
#loan = Loan.new
#loan.book = #book
#loan.user = User.find(loan_params[:user_id])
#loan.status = "loaned"
authorize #loan
if #loan.save
redirect_to :root
else
render :new
end
end
private
def loan_params
params.require(:loan).permit(:user_id)
end
def find_book
#book = Book.find(params[:book_id])
end
end
My form looks like:
<%= simple_form_for([#book, #loan]) do |f| %>
<%= f.input :user_id, collection: #users.map { |user| [user.username, user.id] }, prompt: "Select a User" %>
<%= f.submit %>
<% end %>
If I submit the form without selecting a user, and keep the "Select a User" prompt option, the form is submitted and the app crash because it can't find a user with id=
I don't know why the user presence validation in the form does not work...
you will change your Create method
def create
#loan = Loan.new
#loan.book = #book
#loan.user = User.find_by_id(loan_params[:user_id])
#loan.status = "loaned"
authorize #loan
if #loan.save
redirect_to :root
else
render :new
end
end

many to many relationship between users and books,books wont appear in the view

I am trying to show a list of user's books,but for some reason the saved books won't appear on the view.The application is supposed to allow a signed in user to save books and then show list of books saved by that particular user. Here is my code..
<h1>Home#index</h1>
<%=link_to 'add a book',new_book_path%>
<ul>
<% #books.each do |b|%>
<li><%= b.title %></li>
<li><%= b.author %></li>
<%end%>
</ul>
BOOK CONTROLLER
class BooksController < ApplicationController
def index
end
def create
#book=current_user.books.build(params[:book])
if #book.save
redirect_to '/home/index'
else
render :action =>'new'
end
end
def new
#book=Book.new
end
end
Home controller
class HomeController < ApplicationController
#kip_before_filter :authenticate_user!
#efore_filter :homenticate_user!
def index
#books=current_user.books
end
def show
end
def welcome
end
end
Book model
class Book < ActiveRecord::Base
has_many :book_ownerships
has_many :users,:through => :book_ownerships
end
User model
class User < ActiveRecord::Base
has_many :book_ownerships
has_many :books,:through => :book_ownerships
has_many :skills
# Include default devise modules. Others available are:
# :token_authenticatable, :lockable, :timeoutable and :activatable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation,:firstname
end
Book ownership model
class BookOwnership < ActiveRecord::Base
belongs_to :user
belongs_to :book
end
Book view
<h1>Book#new</h1>
<p>Find me in app/views/book/index.html.erb</p>
<%= form_for(#book) do |f|%>
<%=f.text_field :title%>
<%=f.text_field :author%>
<%=f.submit%>
<%end%>
I assume that the first view in your example actually is Books#index and not Home#index and if that is true then you need to set the variable #books to something in the controller.
It should be enough to make the index action in your BooksController look like this:
def index
#books = current_user.books
end
At least if you are using Rails 3. If you are on an earlier version then you should add the call to the all method like this:
#books = current_user.books.all

Resources