I'm working through an API for Rails and have been fumbling how to set up the associations, the spec, and the controller for my Get route. The goal - As a user I want to get all the notes closest to my location that have not been viewed. I know the viewed? logic is off, as is the Query Interface in the Recipients Model and Nearests Controller.
Here's the Error Rspec is giving me:
Failure/Error: note1 = create(:note)
NoMethodError:
undefined method `recipient_id=' for #<Note:0x007fd2a40e1400>
Here's the spec:
describe 'GET /v1/notes/nearests?lat=&lon=&radius=' do
it 'returns the notes within the given radius' do
near_note1 = create(:note, lat: 37.760322, lon: -122.429667)
near_note2 = create(:note, lat: 37.760322, lon: -122.429667)
lat = 37.771098
lon = -122.430782
radius = 10
get "/v1/notes/nearests?lat=#{lat}&lon=#{lon}&radius=#{radius}"
expect(response_json).to eq([
{
'id' => [near_note1.id, near.note2.id],
# 'lat' => near_note1.lat,
# 'lon' => near_note1.lon,
'note_text' => [near_note1.note_text, near_note2.note_text],
'photo_uri' => [near_note1.photo_uri, near_note2.photo.uri],
# 'expiration' => near_note.expiration.as_json,
'viewed' => [near_note1.viewed?, near_note2.viewed?]
},
])
end
end
Here is the controller code:
def index
#notes = Note.near([
params[:recipient_id],
params[:lat],
params[:lon]],
radius: :APP_CONFIG['radius'],
units: :APP_CONFIG['units']
)
end
Here are the Factories - Notes
FactoryGirl.define do
factory :note do |u|
sender_id {FactoryGirl.create(:user).id}
recipient_id {FactoryGirl.create(:user).id}
lat 1.5
lon 1.5
note_text "MyString"
photo_uri "MyString"
expiration Time.zone.now.utc
end
end
My Models:
User Model
class User < ActiveRecord::Base
has_many :notes
validates :first_name, :last_name, :pw, presence: true
validates :email, :username, :devicetoken, presence: true, uniqueness: true
validates :email, length: { minimum: 8 }
end
Note Model
class Note < ActiveRecord::Base
belongs_to :user, foreign_key: 'sender_id', class_name: 'User'
has_many :recipients, foreign_key: 'recipient_id', class_name: 'User'
validates :sender_id, presence: true
validates :lat, presence: true
validates :lon, presence: true
validates :note_text, presence:true
validates :expiration, presence: true
reverse_geocoded_by :lat, :lon
end
Recipients Model
class Recipient < ActiveRecord::Base
belongs_to :note, foreign_key: 'recipient_id', class_name: 'Note'
def get_recipient
Note.find(:all, params[:note_id])
end
def viewed?
end
end
Related
How to show up all followers that I have?
I have made following possible, but I don't know how to show users that are following. Also I don't know how will I show posts from all the people "I'm following".
#/app/controllers/followers_controller.rb
class FollowersController < ApplicationController
def index
#user = current_user
#followers = Follower.where(following_id: params[:user_id])
end
def create
if current_user
#user = User.find(params[:user_id])
#follower = Follower.new(follower_params)
#follower.user_id = current_user.id
#follower.following_id = #user.id
if #user != current_user
#follower.save
end
redirect_to root_url
end
end
def follower_params
params.require(:follower).permit(:user_id, :following_id)
end
end
#/app/views/followers/index.html.erb
<% #followers.each do |f| %>
<b><%= f.user_id %></b>
<br/>
<% end %>
#/app/models/follower.rb
class Follower < ActiveRecord::Base
validates_uniqueness_of :following_id, scope: :user_id
belongs_to :user
end
#/app/models/user.rb
class User < ActiveRecord::Base
attr_accessor :password
before_save :encrypt_password
before_save { self.username = username.downcase }
before_save { self.email = email.downcase }
validates_confirmation_of :password
validates_presence_of :password, on: :create, length: { minimum: 8 }
validates :email, presence: true, uniqueness: { case_sensitive: false }, length: { maximum: 255 }
validates :username, presence: true, length: { minimum: 6, maximum: 30 }, uniqueness: { case_sensitive: false }
validates :bio, length: { maximum: 140 }
has_many :posts, dependent: :destroy
has_many :comments, dependent: :destroy
has_many :likes, dependent: :destroy
has_many :followers, dependent: :destroy
has_attached_file :avatar,
styles: {
thumb: '75x75#',
small: '150x150#'
}
validates_attachment_content_type :avatar, content_type: /\Aimage\/.*\Z/
has_attached_file :banner,
styles: {
thumb: '75x75>',
small: '150x150>'
}
validates_attachment_content_type :banner, content_type: /\Aimage\/.*\Z/
def self.authenticate(username, password)
user = find_by_username(username)
if user && user.password_hash == BCrypt::Engine.hash_secret(password, user.password_salt)
user
end
end
def encrypt_password
if password.present?
self.password_salt = BCrypt::Engine.generate_salt
self.password_hash = BCrypt::Engine.hash_secret(password, password_salt)
end
end
end
So, there is a little adjustment to be made to your model's setup.
Since a follower is also a user, It means that your Follower(which I will rename to Following should be a join table.)
class Following < ActiveRecord::Base
belongs_to :leader, class_name: 'User'
belongs_to :follower, class_name: 'User'
end
and your user model:
class User < ActiveRecord::Base
has_many :followings, foreign_key: :follower_id,
dependent: :destroy
has_many :leaders, through: :followings
has_many :reverse_followings, foreign_key: :leader_id,
class_name: 'Following',
dependent: :destroy
has_many :followers, through: :reverse_followings
end
Note that you will have to facilitate this by changing your migration to include the required columns.
Then you should be able to call user.followers
and run migrations.
Hope this helps?
I am implementing something of a todo list with a user model and a List model with a date attribute.
On the user show page, I retrieve today's to do list.
How do I go about querying a user todo list for the previous and/or the next day.
All insights are welcome, thanks!
class User < ActiveRecord::Base
before_save { self.email = email.downcase }
before_save { self.username = username.downcase }
has_many :to_do_lists, dependent: :destroy
has_many :tasks, dependent: :destroy
validates_presence_of :first_name, :last_name
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(?:\.[a-z\d\-]+)*\.[a-z]+\z/i
VALID_USERNAME_REGEX = /\A[a-z_0-9]+\z/i
validates :email, presence: true,
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
validates :username, presence: true,
format: { with: VALID_USERNAME_REGEX },
uniqueness: { case_sensitive: false }
def name
[first_name, last_name].compact.join(' ')
end
end
and the list model
class ToDoList < ActiveRecord::Base
belongs_to :user
has_many :tasks, dependent: :destroy
validates_presence_of :user_id
validates :date, presence: true,
uniqueness: {scope: :user_id}
end
Rails adds many helpful methods to Time to make this type of query quite intuitive. Since you validate that a user has only one to do list for each day:
#next_day_list = #user.to_do_lists.find_by_date(Date.today.tomorrow)
#prev_day_list = #user.to_do_lists.find_by_date(Date.today.yesterday)
This simple validation test is failing:
require 'test_helper'
class UserTest < ActiveSupport::TestCase
def setup
#user = User.new(name: "Example User",
email: "user#example.com",
character_attributes: {callsign: "example"},
password: "foobar",
password_confirmation: "foobar"
)
end
test "should be valid" do
assert #user.valid?, "#{#user.errors.messages}"
end
end
...with this message: character.sociable_id"=>["can't be blank"]
I don't understand why the user creation in UserTest is failing to make a valid User.
Each User has_one :character and each Character belongs_to a User.
The User model:
User.rb:
class User < ActiveRecord::Base
attr_accessor :remember_token, :activation_token, :reset_token
has_one :character, as: :sociable, dependent: :destroy
accepts_nested_attributes_for :character
has_secure_password
before_validation do
self.create_character unless character
end
before_save do
self.email.downcase!
end
before_create :create_activation_digest
validates :name, presence: true,
length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(?:\.[a-z\d\-]+)*\.[a-z]+\z/i
validates :email, presence: true,
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
validates :password, length: { minimum: 6 }, allow_blank: true
validates :character, presence: true
.
.
end
The Character model:
Character.rb:
class Character < ActiveRecord::Base
belongs_to :sociable, polymorphic: true
has_many :posts, dependent: :destroy
before_save do
self.callsign.downcase!
end
validates :sociable_id, presence: true
VALID_CALLSIGN_REGEX = /\A[a-z\d\-.\_]+\z/i
validates :callsign, presence: true,
length: { maximum: 20 },
format: { with: VALID_CALLSIGN_REGEX },
uniqueness: { case_sensitive: false }
end
It should be:-
test "should be valid" do
assert #user.valid? , "#{#user.errors.messages}"
end
I've got a form view of an Order model (orders_form.html.erb) with a select option:
<%= f.select :pay_type, PaymentType.array_of_payment_types,
:prompt => 'Select a payment method' %>
PaymentType is another model and .array_of_payment_types is an array created out of the entries in the payment_type_name column, like so:
def self.array_of_payment_types
#array_of_payment_types ||= PaymentType.pluck(:pay_type_name)
end
... from models\payment_type.rb
But I get a proc 'empty?' error:
undefined method `empty?' for #
I hope my problem is clear, it seems like there is an obvious solution but I haven't found one reading other questions so far...
I will update with the relationships in the models...
My models:
payment_type.rb:
class PaymentType < ActiveRecord::Base
attr_accessible :pay_type_name
has_many :orders
validates :pay_type_name, :uniqueness
def self.names
all.collect { |pt| pt.pay_type_name }
end
def self.array_of_payment_types
PaymentType.all.map{ |p| [p.pay_type_name, p.id] }
end
end
order.rb:
class Order < ActiveRecord::Base
attr_accessible :address, :email, :name, :pay_type, :payment_type_id, :cart_id,
:product_id
has_many :line_items, :dependent => :destroy
belongs_to :payment_type
#PAYMENT_TYPES = ['Check','Purchase order','Credit card']
validates :name, :address, :email, :presence => true
validates :pay_type,
:presence => true,
:inclusion => { :in => proc { PaymentType.array_of_payment_types } }
def add_line_items_from_cart(cart)
cart.line_items.each do |item|
item.cart_id = nil
line_items << item
end
end
end
Try using the options_for_select:
# in the view:
<%= f.select :pay_type, options_for_select(PaymentType.array_of_payment_types),
:prompt => 'Select a payment method' %>
# in the PaymentType model:
def self.array_of_payment_types
PaymentType.all.map{ |p| [p.pay_type_name, p.id] }
end
You also need to update your validates statement in the Order model:
validates :pay_type,
:presence => true,
:inclusion => { :in => proc { PaymentType.pluck(:pay_type_name) } }
How can I return errors messages from a cross reference table with multiple records when I trying to create those? I'm trying this:
## activity_set.rb
class ActivitySet < ActiveRecord::Base
has_many :activity_set_lessons
has_many :lessons, :through => :activity_set_lessons
validates :name, :presence => true
def activity_set_lessons=(data)
data.each_with_index do |v, i|
activity_set_lessons.build(
:lesson_id => v[:lesson_id],
:sort_order => i,
:weight_percentage => v[:weight_percentage]
)
end
end
end
## activity_set_lesson.rb
class ActivitySetLesson < ActiveRecord::Base
belongs_to :activity_set
belongs_to :lesson
validates :lesson_id, :presence => true
validates_each :weight_percentage do |record, attr, value|
record.errors.add :base, "woot" if value.blank?
end
end
This is the request data:
## params[:activity_set]
"activity_set" => {
"name" => "hshshshs",
"keywords" => "",
"activity_set_lessons" => [
{"weight_percentage" => "", "lesson_id"=>"4"},
{"weight_percentage" => "", "lesson_id"=>"5"}
]
}
Error messages from #activity_set when I do #save:
{
"errors":{
"activity_set_lessons":["is invalid","is invalid"]
},
"full_messages":[
"Activity set lessons is invalid","Activity set lessons is invalid"
]
}
I always got the same error message even if I'm adding a custom one in the join table. How can I return a message like: "woot 1 is wrong" or something like that, per validation?.
Thanks.
make use of accepts_nested_attributes_for
## activity_set.rb
class ActivitySet < ActiveRecord::Base
has_many :activity_set_lessons
has_many :lessons, :through => :activity_set_lessons
validates :name, :presence => true
accepts_nested_attributes_for :activity_set_lessons
end
view will look like
= form_for #activity_set do |f|
[activity_set form fields ]
= f.fields_for :activity_set_lessons do |p|
= p.select :lession_id
= p.select :weight_percentage