Rails 5 belongs_to scoped validation - validation

lets say I have this model:
class Post < ApplicationRecord
enum post_type: { post: 0, page: 1 }
belongs_to :user
end
by default rails 5 will make the belongs_to :user association to be required. And If you pass optional: true will make this association to be optional. But what I want is the belongs_to :user association to be optional only when the post_type is page and when it is post to required.
How can I do it at the line belongs_to :user ?
At this moment I am doing this:
class Post < ApplicationRecord
enum post_type: { post: 0, page: 1 }
belongs_to :user, optional: true
validates :user_id, presence: { scope: post? }
end
But this will give me an error like:
NoMethodError: undefined method `post?' for #
Is this the correct way to do it? or there is another way?

The user presence can be validated using if option:
validates :user, presence: true, if: :post?

Related

Neo4j and Ruby/Rails: How to only return nodes based on user permissions

How to return an index of only Items where the User has permission?
How to return an index of only Items where the Group a User is in has permission?
How to return a single item only if a User has permission?
How to return a single item only if the Group a User is in has permission?
Note: I know it's possible to query for Item.all, then iterate through the array and pull out only items where .has_permissions == User, but this completely ignores the benefits of having everything in a graph, so is not an answer.
To keep this simple, let's say there are 3 objects:
An Item
A User
A Group
Typical graph situations:
(User)<-[:HAS_PERMISSIONS]-(Item)
(Group)<-[:HAS_PERMISSIONS]-(Item)
(Group)-[:HAS_MEMBERS]->(User)
With the models:
class Item
include Neo4j::ActiveNode
property :name, type: String
property :description, type: String
has_many :out, :user_permission_to, type: :PERMISSION_TO, model_class: :User
has_many :out, :group_permission_to, type: :PERMISSION_TO, model_class: :Group
end
class Identity
include Neo4j::ActiveNode
property :username, type: String
has_many :in, :permission_to, type: :PERMISSION_TO, model_class: :Item
has_many :in, :groups, type: :GROUP_MEMBER, model_class: :Group
end
class Group
include Neo4j::ActiveNode
property :group_name, type: String
has_many :in, :permission_to, type: :PERMISSION_TO, model_class: :Item
has_many :out, :members, type: :GROUP_MEMBER, model_class: :User
end
And with the simple controller:
# GET /items
def index
#items = Item.all
render json: #items
end
# GET /item/1
def show
render json: #item
end
For starters, I'd suggest checking out this article (the second half covers access control which is very similar)
"How to return an index of only Items where the User has permission?"
You could do this a couple of ways. More explicitly:
identity.as(:id).query.match("(id)<-[PERMISSION_TO*1..2]-(item:Item)").pluck(:item)
Alternatively, I think that this would work:
identity.permission_to(rel_length: 1..2)
"How to return an index of only Items where the Group a User is in has permission?"
Simple:
identity.groups.permission_to
"How to return a single item only if a User has permission?"
For the two solutions above:
identity.as(:id).query.match("(id)<-[PERMISSION_TO*1..2]-(item:Item)").limit(1).pluck(:item)
# or
identity.permission_to(rel_length: 1..2).first
"How to return a single item only if the Group a User is in has permission?"
identity.groups.permission_to
Separately, some feedback:
Using the term "index" the way you're using it is a bit confusing because Neo4j has indexes which allow for performant querying of properties on labels.
I would probably make my models like this:
class Item
include Neo4j::ActiveNode
property :name, type: String
property :description, type: String
has_many :in, :users_with_permission, type: :CAN_ACCESS, model_class: :Identity
has_many :in, :groups_with_permission, type: :CAN_ACCESS, model_class: :Group
end
class Identity
include Neo4j::ActiveNode
property :username, type: String
has_many :out, :accessible_items, type: :CAN_ACCESS, model_class: :Item
has_many :out, :groups, type: :IN_GROUP # Don't need `model_class: :Group` here
end
class Group
include Neo4j::ActiveNode
property :group_name, type: String
has_many :out, :accessible_items, type: :CAN_ACCESS, model_class: :Item
has_many :in, :members, type: :IN_GROUP, model_class: :Identity
# You could also do:
# has_many :in, :members, model_class: :Identity, origin: :groups
end

Validation doesn't work in "sexy" style

It seems that Rails doens't let me pass in more than one parameter when using this validation syntax. It always has a syntax method for an unexpected comma after the first argument.
class Apartment < ActiveRecord::Base
geocoded_by :location
after_validation :geocode
has_many :image_attachments
validates_associated :image_attachments
accepts_nested_attributes_for :image_attachments
validates :location, presence: true
validates :description, presence: true
validates :price, :presence => true
,:format => { with: /^[0-9]+$/, message: 'must be a number' }
validates :longitude, presence: true
end
It's bad formatting (and very "unsexy") to have the comma at the beginning of the next line.
Better to do...
validates :price, :presence => true,
:format => { with: /^[0-9]+$/, message: 'must be a number' }
...which should work fine.
A more consistent style is to use the Ruby 1.9 convention for key/value when key is a symbol.
validates :price, presence: true,
format: { with: /^[0-9]+$/, message: 'must be a number' }

FactoryGirl ActiveRecord::RecordInvalid: Validation failed: Name has already been taken

I have three models, Course, Category and partner, a course can have many categories and a course belongs to one partner. When i create my course factory i get the following error:
Partner has a valid factory for course
Failure/Error: expect(FactoryGirl.create(:course)).to be_valid
ActiveRecord::RecordInvalid:
Validation failed: Name has already been taken
Here are my models:
class Category < ActiveRecord::Base
has_many :categorisations
has_many :courses, :through=> :categorisations
belongs_to :user
#validation
validates :name, presence: true , uniqueness: { scope: :name }
end
class Partner < ActiveRecord::Base
has_many :courses
belongs_to :user
validates :name, presence: true, uniqueness: { scope: :name }
validates :short_name, presence: true
VALID_HEX_COLOR= /\A#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})\z/
validates :primary_color, presence: true, format: { with: VALID_HEX_COLOR}
validates :secondary_color, presence: true, format: { with: VALID_HEX_COLOR}
end
class Course < ActiveRecord::Base
extend FriendlyId
friendly_id :title, use: [:slugged, :history]
has_many :categorisations, :dependent => :destroy
has_many :categories, :through=> :categorisations
belongs_to :partner
belongs_to :user
# validates_uniqueness_of :title
validates :title, presence: true
# validates :start_date, presence: true
# validates :duration, presence:true
# validates :state, presence:true
validates :categories, length: { minimum: 1 , message:"please select"}
validates :partner_id, presence: true, allow_nil: false
end
Here are my factories:
factory :partner do |f|
f.name Faker::Name.name
f.short_name "UCT"
f.primary_color "#009bda"
f.secondary_color "#002060"
end
factory :course do |f|
f.title "Introduction to Accounting short course"
f.start_date "2014-02-27 00:00:00"
f.duration "10 WEEKS ONLINE"
partner
categorisation
end
factory :categorisation do |categorisation|
categorisation.category {|category| category.association(:category)}
categorisation.course {|course| course.association(:course)}
end
I am not to sure what i am doing wrong, if anyone could advise me on what the problem may be or the process i can go about fixing this problem may be that would be a great help
try this out:
factory :partner do |f|
f.sequence(:name) { |n| "#{Faker::Name.name} #{n}" }
f.short_name "UCT"
f.primary_color "#009bda"
f.secondary_color "#002060"
end
factory :category do |f|
f.sequence(:name) { |n| "Category #{n}" }
end
All that i had to do was to add the following line to my course factory:
categories {[FactoryGirl.create(:category)]}
couse factory:
factory :course do |f|
f.title "Introduction to Accounting short course"
f.start_date "2014-02-27 00:00:00"
f.duration "10 WEEKS ONLINE"
partner
categories {[FactoryGirl.create(:category)]}
end

Saving nested JSON object to database

Lets say I have a model.
Passengers belongs to Flights.
Flights belongs to Trips
class Trip < ActiveRecord::Base
has_many :flights, :dependent => :destroy, :order => "order_num ASC"
end
class Flight < ActiveRecord::Base
belongs_to :trip, touch: true
has_many :passengers, :dependent => :destroy
end
class Passenger < ActiveRecord::Base
belongs_to :flight, touch: true
end
And I'm getting this sent back to the rails app. (when the user calls save).
*The top level is the trip
{
name: 'Hello Trip',
date: '2013-08-12',
flights: [
{
id: 1
depart_airport: 'RDU',
arrive_airport: 'RDU',
passengers: [
{
user_id: 1,
request: true
}
]
},
{
depart_airport: 'RDU',
arrive_airport: 'RDU',
passengers: [
{
user_id: 1,
request: true
},
{
user_id: 2
request:true
}
]
}
]
}
Right now I'm getting the saved json in and manually looping through the flights to see if there is an id. If there is i'm updating it. If not I'm creating a new one. Then adding the passengers.
I'm wondering if there is an automatic format that Rails takes that can do all the saving for me. I know when you submit a nested form it creates a similar pattern, and adds a _destroy property and the id is a timestamp if it's just created. Would the JSON saving be similar to that?
Thanks for any help!
Yes, you should be able to use accepts_nested_attributes_for here.
You'll need to enable accepts_nested_attributes_for on your models, e.g.,
class Trip < ActiveRecord::Base
has_many :flights, :dependent => :destroy, :order => "order_num ASC"
accepts_nested_attributes_for :flights, :allow_destroy => true
attr_accessible :flights_attributes
end
You'll also need to ensure that your JSON response uses keys that Rails will recognize. You can either modify the JSON response or do something like:
response = JSON.parse(json_string)
response[:flights_attributes] = response.delete(:flights)
# ...
Then you can just do
Trip.create(response)
You'll want to ensure that everything is created/updated as expected. For more on accepts_nested_attributes_for, see the documentation: http://apidock.com/rails/ActiveRecord/NestedAttributes/ClassMethods/accepts_nested_attributes_for.
I think accepts_nested_attributes_for is convenient, but note that there are some that think it should be deprecated (e.g., here: http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/, see here for a response: https://stackoverflow.com/a/17639029/1614607).

Routing Error Rails, dont undertand

Are all correct but the error persists, i make the all codes to all you see
Routing Error
No route matches {:controller=>"posts", :user_id=>#<Post id: 1, title: "sa", content: "sasa", created_at: "2013-01-06 00:25:03", updated_at: "2013-01-06 00:25:03", image_file_name: nil, image_content_type: nil, image_file_size: nil, image_updated_at: nil, user_id: 1>, :action=>"edit"}
Try running rake routes for more information on available routes.
My routes are:
get "painel/index"
resources :posts
resources :user do
resources :posts,:comments
end
and my models is:
user.rb
attr_accessible :email, :password, :password_confirmation,:username
has_many :posts
has_many :comments
post.rb
attr_accessible :content, :title ,:image
has_many :comments
belongs_to :user
1) Check 'rake routes'
2) use the url or path you get in routes list. That will solve your error
If that does not solve your error,
paste your routes file and link_to call which is returning error.

Resources