Displaying many-to-many using select multiple - ruby-on-rails-4

right now im building a form that accepts a many to many using a select multiple. after creating the form, im trying to display the information collected in the SHOW page however i dont know how to display that data in embedded ruby. join model is CardTypesList
Models
class Card < ActiveRecord::Base
self.inheritance_column = nil
validates :name, presence: true, uniqueness: {case_sensitive: false}
has_many :card_type_lists
has_many :card_types, through: :card_type_lists
accepts_nested_attributes_for :card_type_lists
end
class CardType < ActiveRecord::Base
has_many :card_type_lists
has_many :cards, through: :card_type_lists
end
class CardTypeList < ActiveRecord::Base
belongs_to :cards
belongs_to :card_types
accepts_nested_attributes_for :card_type
end
NEW form using select
<%= f.label :types %>
<%= f.select :card_type_ids, CardTypes.all.collect{|x| [x.name, x.name]}, {},{:title => "Select a Type", :multiple => true, :class => 'selList'} %>
Embedded ruby trials
<td class="card-td"><%= #card.card_types %></td>
Expected: ["Type 1", "Type2"]
this renders the page, but yields (in text):
<ActiveRecord::Associations::CollectionProxy::ActiveRecord_Associations_CollectionProxy_CardTypes:0x00000102f58a18>
EDIT: schema added.
ActiveRecord::Schema.define(version: 20140120042152) do
create_table "card_type_lists", force: true do |t|
t.integer "card_type_id"
t.integer "card_id"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "card_types", force: true do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "cards", force: true do |t|
t.string "name"
t.string "set"
t.string "card_types"
t.string "colors"
t.string "cost"
t.string "rarity"
t.string "oracle"
t.float "value"
t.integer "number_owned"
t.string "notes"
t.string "img_link"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "types_mask"
end
add_index "cards", ["name"], name: "index_cards_on_name", unique: true
end
and this is my controller:
class CardsController < ApplicationController
def new
#card = Card.new
#card.card_type_lists.build.build_card_type
end
def show
#card = Card.find(params[:id])
end
def create
#card = Card.new(card_params)
if #card.save
redirect_to #card
else
render 'new'
end
end
private
def card_params
params.require(:card).permit(:name, :set, {:card_types => []}, :color, :cost, :rarity,:oracle,:value, :number_owned,:notes)
end
end

Should be:
class CardTypeList < ActiveRecord::Base
belongs_to :card
belongs_to :card_type #Singular
end
Some more things to think about:
Does your join model have the correct columns?
You should use accepts_nested_attributes_for
Look at how you're calling the data
Schema
Join models in has_many :through have to have foreign_key references to both models they're joining. The way you do this is to use a schema like this:
card_types_lists
id | card_id | card_type_id | other | information | created_at | updated_at
When you mentioned the error no such column: card_type_lists.card_types_id, it generally means you either don't have the correct column in the db, or your reference is incorrect. Looking at it, it's your association (referencing plural instead of singular)(fixed above)
Forms
Something you should consider is using accepts_nested_attributes_for to send the correct data to the nested models
This is when you want to create some records in either model, and works by allowing you to define "new" objects for your other models in your parent model, passing the data to your child models, like this:
#app/models/card.rb
Class Card < ActiveRecord::Base
has_many :card_type_lists
has_many :card_types, through: :card_type_lists
accepts_nested_attributes_for :card_type_lists
end
#app/models/card_type_list.rb
Class CardTypeList < ActiveRecord::Base
belongs_to :card
belongs_to :card_type
accepts_nested_attributes_for :card_type
end
#app/controllers/cards_controller.rb
def new
#card = Card.new
#card.card_types_lists.build.build_card_type
end
Data
If you want to show your associative data correctly, you should try this:
#app/views/cards/show.html.erb
<%= #card.card_types %>

Related

Is this Rails association between Company and Employee valid?

I'm writing a basic CRM app to learn Ruby on Rails 4.2, and I have an odd pair of associations (or maybe not so odd). If this were straight SQL and another platform it would be straightforward, but I'm not sure if I'm doing it properly in ROR.
The business requirement is that each company has one or more users. Simple enough. (Relevant portions of the model below.)
user.rb
belongs_to :company
company.rb
has_many :users
An additional requirement is that each company can have one primary user. I need to be able to reference the user's info (name, email, etc) within a Companies index and show view. This is where I'm kind of uncertain. Should the models look like this (it doesn't make sense to me, but I can't think of another way)?:
user.rb
belongs_to :company
company.rb
has_many :users
has_one :user
Here's an abbreviated schema. The user_id in companies is the FK to store the "primary contact":
create_table "companies", force: :cascade do |t|
t.string "companyname"
t.integer "user_id"
end
create_table "users", force: :cascade do |t|
t.string "username"
t.string "email"
t.string "phone"
t.integer "company_id"
end
Thank you for your help.
user.rb
belongs_to :company
company.rb
has_many :users
**belongs_to :primary_user, class_name: "User"**
in company you should have primary_user_id column. You can also define has_one on user like this:
user.rb
belongs_to :company
**has_one :primary_company, class_name: "Company", foreign_key: "primary_user_id"**
company.rb
has_many :users
belongs_to :primary_user, class_name: "User"
user.rb
belongs_to :company
company.rb
has_many :users
Above models are proper. I think for the primary user of a company you can have one more model like primary_user_membership.rb
belongs_to :company
belongs_to :user
create_table "primary_user_memberships", force: :cascade do |t|
t.integer "company_id"
t.integer "user_id"
end
Based on your info, your model is correct.
user.rb
belongs_to :company
company.rb
has_many :users
You can do Company.first.users to see all the first company's users.
Let's say User 1 is the Primary user of Company 1, in rails console, you can check by doing Company.first.users.first
You can also check User 1's info by doing :-
user1 = Company.first.users.first
user1.username
When you generate the User model, you can do: rails g model User username email phone company:belongs_to
Your schema model should look like:-
create_table "companies", force: :cascade do |t|
t.string "companyname"
end
create_table "users", force: :cascade do |t|
t.string "username"
t.string "email"
t.string "phone"
t.integer "company_id"
end

filter api index action using the ID in the URI pattern

Question how can i filter my get api function using the ID ni my URI pattern
for example if the client side uses the localhost:3000/users/1/projects
he will get the projects with a field of "user_id" equal to the URI pattern /users/:id
#routes
resources :users do
resourcers :projects
end
#project controller
def index
#projects = Project.all
render json: #projects
end
#users assocciation
has_many :projects, dependent: :destroy
has_many :tasks, dependent: :destroy
has_many :comments, dependent: :destroy
#project association
class Project < ActiveRecord::Base
has_many :task, dependent: :destroy
belongs_to :user
end
#DB schema
create_table "projects", force: :cascade do |t|
t.string "name"
t.text "descriptions"
t.date "startDate"
t.date "dueDate"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
t.boolean "project_confirmation"
end
what we want is when the client side uses this route /users/:user_id/projects(.:format) he accepts projects with field of user_id similar to the routes /users/:id
Use AMS or Jbuilder for that.
https://github.com/rails-api/active_model_serializers
https://github.com/rails/jbuilder

Find or Initialize By Parameter Confusion Rails 4

I'm trying to use 'find_or_initialize_by' in my orders controller. I need to find the record by two parameters, user_id and item_id. If that particular order combination doesn't exist then I would like to create it.
Here is what I have in the orders controller:
def create
#order = Order.find_or_initialize_by(#not sure of syntax here)
#order.update_attributes(quantity_requested)
#order.save
redirect_to :back
end
I've tried many different parameters and all produce error messages. Can anyone please clarify the correct way to pass parameters here?
Thank you for any and all help.
Edit to add more code:
db schema:
create_table "orders", force: true do |t|
t.integer "item_id"
t.integer "quantity_requested"
t.boolean "order_completed"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
Order model:
class Order < ActiveRecord::Base
belongs_to :user
has_one :item, dependent: :destroy
validates :user_id, presence: true
end
Item show:
<%= form_for Order.new do |f| %>
<%= f.number_field :quantity_requested, value: 0, max: #item.quantity, min: 1, class: "item-quantity form-control", :onclick => '$(this.form).submit()' %>
<% end %>
find_or_initialize_by takes two parameters. The first is a hash, and the second is a block to be used if nothing is found and is instead initialized. If you don't need the block, you can just do:
#order = Order.find_or_initialize_by(user_id: value, item_id: value)

fields_for a nested resource not working

I have the following associations:
class Campaign < ActiveRecord::Base
has_many :events
end
class Event < ActiveRecord::Base
belongs_to :campaign
belongs_to :venue
accepts_nested_attributes_for :venue
end
class Venue < ActiveRecord::Base
has_many :events
end
And I have the following form:
<%= form_for([#campaign, #event]) do |f| %>
<%= f.fields_for :venue do |v| %>
<%= v.text_field :search %>
<% end %>
<% end %>
Anything in the fields_for block is not showing up. But the funny thing is if i change f.fields_for :venue to f.fields_for :venues, the :search field shows up.
But :venues shouldn't be plural on :events. Even in rails console Event.venues does not work
NoMethodError: undefined method `venues'
It also generates the wrong field names
This makes me think I have my model associations jacked up, but after reviewing the docs. it does not seem so. Here is my schema for reference:
create_table "campaigns", force: true do |t|
t.integer "user_id"
t.string "title"
t.text "description"
t.integer "image_id"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "events", force: true do |t|
t.integer "campaign_id"
t.datetime "start_date"
t.datetime "end_date"
t.integer "venue_id"
t.integer "image_id"
t.datetime "created_at"
t.datetime "updated_at"
t.string "title"
t.text "description"
end
create_table "venues", force: true do |t|
t.float "latitude"
t.float "longitude"
t.string "address1"
t.string "address2"
t.string "city"
t.string "zip"
t.string "state"
t.string "country"
t.string "name"
t.string "gid"
t.datetime "created_at"
t.datetime "updated_at"
end
See the similar SO Posts
1.Does accepts_nested_attributes_for work with belongs_to?
2.Getting fields_for and accepts_nested_attributes_for to work with a belongs_to relationship
As mentioned in one of those posts,one way of achieving it is changing the accepts_nested_attributes_for to the has_many side
class Campaign < ActiveRecord::Base
has_many :events
accepts_nested_attributes_for :events
end
class Event < ActiveRecord::Base
belongs_to :campaign
belongs_to :venue
end
class Venue < ActiveRecord::Base
has_many :events
accepts_nested_attributes_for :events
end
If it is not,you might want to build a venue for events with #event.build_venue in the controller before calling fields_for.
Hope it helps!

How do I hide from a user the ability to perform an action on a resource after a period of time in Rails 4?

For my projects table I have a column, state, which take string values: "open", "in production", "pending approval" and "completed". The actions that trigger changes between these states are messages that go to and from users about the projects or actions associated to messages. When the object, project, goes from "open" to "in production", in the users inbox a user has a button appearing on the conversation associated with the project that reads, "Drop Project." Moreover, I can wire up the functionality as well. Clicking this button will disassociate you with the project.
My question: how can I have this button hide itself or disappear after 5 days of the project's state, having gone from "open" to "in production"? In other words, I want the user to have the opportunity to disassociates his self from the project within a certain time limit. After that, he is stuck with the project and is encouraged to finish it.
Also, how would I test this with rspec? Is this strictly with an integration test, or can I test it with a unit test too?
I am aware of this SO post (Rails 3 Check if attribute changed), but it has not helped me figure out the solution to my problem. However, I feel like it could serve as support.
Here is my db schema and models:
ActiveRecord::Schema.define(version: 20140514191454) do
create_table "conversations", force: true do |t|
t.datetime "created_at"
t.datetime "updated_at"
t.integer "user_id"
end
create_table "organizations", force: true do |t|
t.string "name"
t.datetime "ruling_year"
t.text "mission_statement"
t.string "guidestar_membership"
t.string "ein"
t.string "street1"
t.string "street2"
t.string "city"
t.integer "state_id"
t.string "zip"
t.integer "ntee_major_category_id"
t.string "funding_method"
t.integer "user_id"
t.string "cause"
end
create_table "private_messages", force: true do |t|
t.integer "sender_id"
t.integer "recipient_id"
t.string "subject"
t.text "body"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "project_id"
t.integer "conversation_id"
end
create_table "project_users", force: true do |t|
t.integer "user_id"
t.integer "project_id"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "projects", force: true do |t|
t.string "title"
t.text "description"
t.string "skills"
t.string "causes"
t.datetime "deadline"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "user_id"
t.integer "organization_id"
t.integer "estimated_hours"
t.string "state"
end
create_table "user_conversations", force: true do |t|
t.integer "user_id"
t.integer "conversation_id"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "users", force: true do |t|
t.integer "organization_id"
t.string "first_name"
t.string "last_name"
t.string "email"
t.string "interests"
t.string "skills"
t.string "street1"
t.string "street2"
t.string "city"
t.integer "state_id"
t.integer "phone_number"
t.string "zip"
t.boolean "organization_administrator"
t.boolean "organization_staff"
t.boolean "volunteer"
t.datetime "created_at"
t.datetime "updated_at"
t.string "password_digest"
t.string "position"
t.integer "project_id"
t.string "time_zone"
end
end
Conversation
class Conversation < ActiveRecord::Base
has_many :private_messages, -> {order('created_at ASC')}
def sender_user_name_of_recent_message
message = self.private_messages.last
user = message.sender_id
name = User.find_by(id: user)
"#{name.first_name} #{name.last_name}"
end
def the_id_of_sender
message = self.private_messages.last
user = message.sender_id
name = User.find_by(id: user)
name.id
end
def private_message_subject
message = self.private_messages.last
message_subject = message.subject
end
def private_message_body
message = self.private_messages.last
message_body = message.body
end
def join_request
message = self.private_messages.first
project = Project.find_by(id: message.project_id)
if project
project.state == "open"
end
end
def project_complete_request
message = self.private_messages.first
project = Project.find_by(id: message.project_id)
if project
project.state == "pending approval"
end
end
def opportunity_drop_project
message = self.private_messages.first
project = Project.find_by(id: message.project_id)
if project
project.state == "in production"
end
end
end
User
class User < ActiveRecord::Base
has_secure_password validations: false
belongs_to :organization
belongs_to :project
has_many :project_users
has_many :projects, through: :project_users
has_many :sent_messages, class_name: 'PrivateMessage', foreign_key: 'sender_id'
has_many :received_messages, -> {order('created_at DESC')}, class_name: 'PrivateMessage', foreign_key: 'recipient_id'
has_many :conversations
def private_messages
messages = self.sent_messages + self.received_messages
messages.sort!
end
def user_conversations
collection = self.received_messages.select(:conversation_id).distinct
all_conversations = collection.map do |member|
convo_id = member.conversation_id
Conversation.find_by(id: convo_id)
end
all_conversations.sort
end
def organization_name
organization.name
end
end
Organization
class Organization < ActiveRecord::Base
belongs_to :organization_administrator, foreign_key: 'user_id', class_name: 'User'
has_many :projects
has_many :users
end
PrivateMessage
class PrivateMessage < ActiveRecord::Base
belongs_to :recipient, foreign_key: 'recipient_id', class_name: 'User'
belongs_to :sender, foreign_key: 'sender_id', class_name: 'User'
belongs_to :conversation
validates_presence_of :subject, :body
end
Project
class Project < ActiveRecord::Base
belongs_to :organization
has_many :project_users
has_many :users, through: :project_users
def project_admin
organization.organization_administrator
User.find(organization.organization_administrator.id)
end
def open
self.state == "open"
end
end
ProjectUser
class ProjectUser < ActiveRecord::Base
belongs_to :user
belongs_to :project
end
I don't think there is a built-in Rails way to do this. ActiveModel::Dirty won't work because it only tells you if an object attribute has been changed in memory, once the object is saved to the DB it isn't dirty anymore. I think you'll just have to create a timestamp companion column for state and reset that value to the current time every time you change state. Then you'd just check against the time difference between now and the state_changed attribute.
You can do this by assigning Time.now to the state_changed attribute every time you assign a new state. Or, alternately, you could use a before_save callback on the project object. Use AM::Dirty to check if project.state has been changed, and set the state_changed attribute to Time.now if it has been. That way would be more DRY.
And then in the code for the actions you want time dependent you'd wrap them in a conditional like
if project.state == "in production" && project.state_changed < 5.days.ago
And you'd also create a helper for your views that made the same calculation and only show the button if it is true.
I was missing some large objects here. A user has many projects through volunteer applications and a user has many projects through contracts.
Seeing as that accepting a user's solicitation on a project occurs in the user's inbox which contains an array of conversations that have a foreign key, contract_id, I implemented the following method in the Conversation model:
def with_opportunity_to_drop_job
contract = Contract.find(self.contract_id)
contract.active && contract.work_submitted == false && contract.created_at > 5.days.ago
end
If the creation date is more than 5 days ago, the button disappears -- luckily I have tests that validate this so I do not need to wait around for 5 days :-)