Querying for a relationship in ruby on rails and update results via Ajax - sql

I have a ROR app that has many players, and many proposed games. The games display on a feed and a player can decide to hide them from this feed. The hidden function works like this:
in player.rb:
has_many :hides, :foreign_key=> "hider_id",
:dependent => :destroy
has_many :hidees, :through => :hides
def hidden?(hidee)
hides.find_by_hidee_id(hidee)
end
def hide!(hidee)
hides.create!(:hidee_id => hidee.id)
end
def unhide!(hidee)
hides.find_by_hidee_id(hidee).destroy
end
hides_controller.rb
class HidesController < ApplicationController
def create
#game = Game.find(params[:hide][:hidee_id])
current_profile.hide!(#game)
redirect_to :back
end
def destroy
#game = Hide.find(params[:id]).hidee
current_profile.unhide!(#game)
redirect_to :back
end
end
hide.rb
class Hide < ActiveRecord::Base
attr_accessible :hidee_id
belongs_to :hider, :class_name => "Player"
belongs_to :hidee, :class_name => "Game"
validates :hider_id, :presence => true
validates :hidee_id, :presence => true
end
game.rb
has_many :reverse_hides, :foreign_key => "hidee_id",
:class_name => "Hide",
:dependent => :destroy
has_many :hiders, :through => :reverse_hides
routes.rb
resources :games do
member do
post :publish
post :unpublish
get :view
get :hidees, :hiders
end
I'm trying to do two things: 1. Write a function that would allow me to hide a game from the feed if a relationship between hidden relationship between game and player exits, and 2. write a "show hidden" button that would allow me to return all projects that were "hidden" by the player.
So far with part 1. I have the following code in the view, and while this does the trick in terms of setting up the relationships, it does not "hide" the game from the feed--I'm guessing I would need ajax for that??
- if current_profile.hidden?(game)
= form_for current_profile.hides.find_by_hidee_id(game), :html => { :method => :delete } do |f|
= f.submit "Unhide", :title => "Unhide this game."
- else
= form_for current_profile.hides.build(:hidee_id => game.id) do |f|
= f.hidden_field :hidee_id
= f.submit "Hide", :title => "Hide this game"
Thank you so much for viewing this, I know it's quite long, but I would appreciate any help you could offer. Also, thank you for you time.

Related

Limiting how often a user can post on a particular person's profile/wall in Rails

How can I limit a user to only to being able to post once or twice per day on a particular users's wall? I primarily want to do it in order to limit spam. My code for the wall, models, view, and controllers are below. I don't really know how to go about it as I'm new to rails but I know there is something time.now. I'm not exactly sure how to implement such a feature.
Class UsersController < ApplicationController
def show
#user = User.find(params[:id])
#first_name = #user.first_name
#last_name = #user.last_name
#wallpost = WallPost.new(params[:wall_post])
#showwallposts = #user.received_wallposts
end
def create
#wallpost = WallPost.create(params[:wall_post])
end
models
class WallPost < ActiveRecord::Base
attr_accessible :content, :receiver_id, :sender_id
belongs_to :receiver, :class_name => "User", :foreign_key => "receiver_id"
belongs_to :sender, :class_name => "User", :foreign_key => "sender_id"
end
class User < ActiveRecord::Base
has_many :sent_wallposts, :class_name => 'WallPost', :foreign_key => 'sender_id'
has_many :received_wallposts, :class_name =>'WallPost', :foreign_key => 'receiver_id'
in the view
<%= form_for(#wallpost, :url => {:action => 'create'}) do |f| %>
<%= f.hidden_field :receiver_id, :value => #user.id %>
<%= f.hidden_field :sender_id, :value => current_user.id %>
<%= f.text_area :content, :class => 'inputbox' %>
<%= f.submit 'Post', class: 'right btn' %>
<% end %>
You could create a custom validator which assures maximum DAILY_LIMIT posts have been created on that person's wall that day by that user:
class SpamValidator < ActiveModel::Validator
DAILY_LIMIT = 2
def validate(record)
if similar_posts_today(record).count >= DAILY_LIMIT
record.errors[:spam_limit] << 'Too many posts today!'
end
end
def similar_posts_today(record)
WallPost.where(receiver: record.receiver, sender: record.sender)
.where("DATE(created_at) = DATE(:now)", now: Time.now)
end
end
Then add that validation to your WallPost model:
validates_with SpamValidator
Then it will fail with a validation error when trying to create a wall post beyond the limit set in the constant. You need to handle this case in the create action in your controller. A simple (but not optimal in terms of user experience) way of handling this is:
def create
#wallpost = WallPost.new(params[:wall_post])
flash[:error] = "You've reached the daily posting limit on that wall." unless #wallpost.save
redirect_to user_path(#wallpost.receiver)
end
With that, it'll try to save the new wall post, if it is unable to, it'll set flash[:error] to the error message above. You'd need to show this on your show.html.erb page with <%= flash[:error] if flash[:error] %>.

Nested Form: Can’t populate join table between parent and child if child exists / Couldn't find Child with ID=1 for ParentChildJoin with ID=

I cleaned up my code, it looks much nicer now, but still doesn’t work. It starts to be a pain…
I just can’t save a parent with an existing child in nested form with parent has_many childs :through joinmodel.
In my case a Project has_many contributing Teachers and many contributing Pupils, both are Join-Models to Users. A Project has_many Schools as well.
(May be I should better name the models Teacherize and Pupilize or ProjectTeacher and ProjectPupil.)
As long as all records are new it all works fine. As soon as I want to connect an existing User as new Teacher of a new Project I get the following error:
Couldn't find User with ID=1 for Teacher with ID=
(1 is the correct user ID)
The problem should be somewhere her in my helper to setup empty form fields:
At least I guess so...
module ProjectsHelper
def setup_project(project)
if project.teachers.length <= 0 # usually there is just one teacher, so add one if there isn't one
teacher = project.teachers.build(:role_in_project => 'master')
if user_signed_in?
#teacher = project.teachers.new(:role_in_project => 'master', :user => current_user)
teacher.user = current_user # associate first teacher with current_user
else
#teacher = project.teachers.build
teacher.user = User.new # associate first teacher with a new user instance
end
end
if project.project_schools.length <= 0 # usually there is just one school, so add one if there isn't one
project_school = project.project_schools.build
project_school.school = School.new
end
if project.pupils.length < 3 # There can be up to 3 people, so add a blank fieldset as long as there are less than 3
pupil = project.pupils.build
pupil.user = User.new
end
project
end
end
These are my params received:
{"utf8"=>"✓", "authenticity_token"=>"uCCMk/s3SpDfR7+fXcsCOHPvfvivBQv8pVFVhdh6iro=",
"project"=>{
"teachers_attributes"=>{
"0"=>{
"id"=>"",
"user_attributes"=>{
"id"=>"1",
"gender"=>"male",
"title"=>"",
"firstname"=>"Firstname1",
"name"=>"Lastname1",
"faculty"=>"",
"fon"=>"",
"fax"=>""}
}
},
"id"=>"",
"title"=>"First Project",
"description"=>"This is a foo bar project!",
"presentation_type"=>"experimentell",
"note"=>""
},
"commit"=>"Register Project",
"action"=>"create",
"controller"=>"projects"
}
The case isn’t too abstract; it has to be possible to achieve it.
It’s just to connect a new parent record with an existing child record!
In this article, which is very good, exactly the case is explaint:
http://rubysource.com/complex-rails-forms-with-nested-attributes
# app/helpers/form_helper
module FormHelper
def setup_user(user)
user.address ||= Address.new
(Interest.all - user.interests).each do |interest|
user.interest_users.build(:interest => interest)
end
user.interest_users.sort_by! {|x| x.interest.name }
user/tmp/clean-controllers.md.html
end
end
There the interest is existing and gets connected through a new record in interest_uesers.
Why do I get the error when trying to do the same thing?
project.teachers.build(:user => current_user)
I studied several articles and casts, but none of them connect existing childs.
http://railscasts.com/episodes/196-nested-model-form-revised
http://apidock.com/rails/ActionView/Helpers/FormHelper/fields_for
http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
Rails 3.1+ Nested Forms Issue: Can't mass-assign protected attributes
Trying to use accepts_nested_attributes_for and has_and_belongs_to_many but the join table is not being populated
Quote: “accepts_nested_fields_for is used to create and modify related objects in a form. It can be used to populate join table, which is kind of what you're trying to do. However, using accepts_nested_fields_for to populate the join table is impossible with a HABTM relationship.”
That’s what I wanna do! Populate the join table!
It starts to be frustrating and I’d be glad to get some help!
My Models
class Project < ActiveRecord::Base
attr_accessible :title, :description, :presentation_type, :note,
:project_schools_attributes, :schools_attributes, :teachers_attributes, :pupils_attributes,
:users_attributes
validates_presence_of :title
validates_presence_of :description
validates_presence_of :presentation_type
has_many :project_schools, :dependent => :destroy
accepts_nested_attributes_for :project_schools
has_many :schools, :through => :project_schools
#accepts_nested_attributes_for :schools, :reject_if => :all_blank
has_many :pupils, :dependent => :destroy
accepts_nested_attributes_for :pupils, :reject_if => :all_blank
has_many :users, :through => :pupils
has_many :teachers, :dependent => :destroy
accepts_nested_attributes_for :teachers, :reject_if => :all_blank
has_many :users, :through => :teachers
#accepts_nested_attributes_for :users, :reject_if => :all_blank
end
class ProjectSchool < ActiveRecord::Base
attr_accessible :role_in_project, :comment,
:school_attributes, :school_id, :project_id
belongs_to :school
accepts_nested_attributes_for :school
belongs_to :project
end
class School < ActiveRecord::Base
attr_accessible :email, :fax, :fon, :name, :place, :street, :type_of_school, :www, :zip
has_many :project_schools
has_many :projects, :through => :project_schools
has_many :users # in real live they are named teachers and pupils but in this case the association goes directly to a user_id, not to teacher/pupil model
validates_presence_of :name, :type_of_school, :street, :place, :zip, :fon
validates :email, :format => { :with => /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i, :on => :create }, :allow_blank => true
end
class Teacher < ActiveRecord::Base
attr_accessible :role_in_project, :user, :user_attributes, :project_id, :user_id
belongs_to :project
belongs_to :user
accepts_nested_attributes_for :user
serialize :role_in_project
end
class Pupil < ActiveRecord::Base
attr_accessible :classname, :user_attributes #, :project_id, :user_id
belongs_to :project
belongs_to :user
accepts_nested_attributes_for :user
end
class User < ActiveRecord::Base
serialize :roles
belongs_to :school
has_many :teachers
has_many :pupils
has_many :projects, :through => :teachers
has_many :projects, :through => :pupils
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :gender, :firstname, :name, :street, :place, :title, :faculty, :assignment,
:classname, :zip, :fon, :fax, :school_id, :roles,
:password, :added_by_user_id, :password_confirmation, :remember_me
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable#, :validatable
after_initialize :init
def init
# the default guest user
self.roles ||= ['default'] #will set the default value only if it's nil
end
end
My controller
class ProjectsController < ApplicationController
require 'axlsx'
before_filter :load_page, only: [:show, :index, :destroy]
def new
#project = Project.new
end
def create
#project = Project.new(params[:project])
respond_to do |format|
if #project.save
sign_in(:user, #project.teachers[0].user) unless user_signed_in?
# TODO: send mail
# save as excel file in dropbox
save_in_dropbox(#project)
format.html { redirect_to #project, notice: t('project.was_created') }
else
logger.debug #project.errors.inspect
format.html { render action: "new" }
end
end
end
end
projects/_form.html.haml
%h1
= t('project.register_headline')
= simple_form_for( setup_project(#project), :html => {:class => 'form-horizontal'} )do |f|
= f.error_notification
#teachers-wrapper.well
%span.jumpanchor#lehrkraft_anchor
%fieldset.form-inputs
= f.simple_fields_for :teachers do |teacher|
= render "teacher_fields", :f => teacher
.school-wrapper.well
%span.jumpanchor#schule_anchor
%h2
Informationen zur Schule
%fieldset.form-inputs
= f.simple_fields_for :project_schools do |project_school|
= render "school_fields", :f => project_school
.project-wrapper.well
%span.jumpanchor#projekt_anchor
%h2
Informationen zum Projekt der Schüler
%fieldset.form-inputs
= f.hidden_field :id
= f.input :title, :input_html => { :class => 'span6' }
= f.input :description, :input_html => { :class => 'span6', rows: 5 }
= f.input :presentation_type, collection: ['theoretisch', 'experimentell'], as: :radio_buttons, :class => 'controls-row', :input_html => { :class => 'inline' }
.clearfix
= f.input :note, :input_html => { :class => 'span6', rows: 3 }
.pupils-wrapper.well
%span.jumpanchor#schuler_anchor
%fieldset.form-inputs
= f.simple_fields_for :pupils do |pupil|
= render "pupil_fields", :f => pupil
projects/_teacher_fields.html.haml
= f.simple_fields_for :user do |user|
=# render "teacher_user_fields", :f => user
%h2
Betreuende Lehrkraft
- if user_signed_in?
= user.input :email, :disabled => true, :input_html => {:class => 'email_validation'}
- else
= user.input :email, :autofocus => true, :input_html => {:class => 'email_validation'}, :hint => 'Dies muß Ihre eigene E-Mailadresse sein!'
.details
=# user.hidden_field :id
= user.input :id
= user.input :gender, collection: [:female, :male]
= user.input :title
= user.input :firstname
= user.input :name
= user.input :faculty
= user.input :fon
= user.input :fax
It is Rails 3.2 with Ruby 1.9.3

cant setup an active admin resource form with has many :through assoc

I'm working on a rails (3.7.8) app and using active admin to manage resources for the ff models:
class AdminUser < ActiveRecord::Base
has_many :user_article_categories, :include => :article_categories
has_many :article_categories, :through => :user_article_categories,
:source => :admin_user
has_many :articles, :through => :user_article_categories,
:source => :admin_user
# ...
end
class UserArticleCategory < ActiveRecord::Base
belongs_to :admin_user
belongs_to :article_category
attr_accessible :admin_user_id, :article_category_id, :included
attr_accessor :included
after_find :set_included
private
def set_included
self.included = "1"
end
# ...
end
the "included" attribute was based on a solution presented here
class ArticleCategory < ActiveRecord::Base
has_many :user_article_categories, :include => :admin_users
has_many :admin_users, :through => :user_article_categories,
:source => :article_category
has_many :articles, :through => :user_article_categories,
:source => :article_category
# ...
end
but I seem not to get setting up (correctly) a form for admin_users, such that creating a new admin_user would have all article_categories displayed as a list of checkboxes
while a persisted admin_user for update would have all article_categories checkboxes displayed but wit all previously set article-categories checked, so that an update would remove unchecked checkboxes and add newly checked ones to what goes to the join-table
for admin/admin_users.rb I create the form as follows, this does not work, though it renders correctly, any help will be appreciated
form do |f|
if f.object.persisted? and current_admin_user.id == f.object.id
f.inputs "Admin Details" do
f.input :email
f.inputs :for => user_article_categories do |usr_art_catr|
usr_art_catr.input :article_category_id, :hidden
usr_art_catr.input :included
end
end
else
f.inputs "Admin Details" do
f.input :email
f.input :superuser, :label => "Super User Priveleges"
f.input :article_categories, :as => :check_boxes,
:collection => ArticleCategory.select("id, name")
end
end
f.buttons
end
Actually, to display a list of checkboxes of all article_categories and check all already checked article categories for a given admin_user on update.
Formtastic, when rendering the show form for the form's object, calls a method provided on the form object via
f.input :method_to_be_called, :as => :checkboxes
which formtastic would compare its result with a collection provided via
the
:collection => any_valid_ruby_object
but both should return the same kinds; array/array or hash/hash, whatever, to determine which checkboxes should be checked, by performing a difference on the two collections.
The method called by formtastic could be an instance method on admin_user that queries the join-table, to determine which checkboxes should be checked and builds an array of that from the related article_categories table or returns an empty array when there is none.
This allows formtastic do what is right, as least in this context. This solution makes the "included" attribute on user_article_categories (the join-table) redundant!

How to handle multiple nested resources in ActiveAdmin?

I'm using ActiveAdmin (0.4.0) with Rails (3.1.1).
I can't find a nice way/hack to handle multiple nested resources.
Considerer 3 models as:
class Program < ActiveRecord::Base
has_many :knowledges, :dependent => :destroy
end
class Knowledge < ActiveRecord::Base
belongs_to :program
has_many :steps, :dependent => :destroy
end
class Step < ActiveRecord::Base
belongs_to :knowledge
end
And the ActiveAdmin resources:
ActiveAdmin.register Program do
end
ActiveAdmin.register Knowledge do
belongs_to :program
end
ActiveAdmin.register Step do
belongs_to :knowledge
end
In routes.rb:
namespace :admin do
resources :programs do
resources :knowledges do
resources :steps
end
end
end
Here's the urls for the index of the programs, the knowledges and the steps :
http://localhost:3000/admin/programs
http://localhost:3000/admin/programs/1/knowledges
http://localhost:3000/admin/programs/1/knowledges/1/steps
No problem for the "Knowledge" admin but the "Step" admin don't keep the nested context.
For example, when I use filters in steps#index I'm redirected to:
http://localhost:3000/admin/knowledges/1/steps?params...
But it must have been:
http://localhost:3000/admin/programs/1/knowledges/1/steps?params...
Same problem when I create a new resource:
http://localhost:3000/admin/knowledges/1/steps/new
Instead of:
http://localhost:3000/admin/programs/1/knowledges/1/steps/new
Same problem with the breadcrumb... etc...
What I've tried so far in app/admin/steps.rb:
ActiveAdmin.register Step do
belongs_to :knowledge
config.clear_action_items!
action_item :only => :index do
link_to('Create Step', new_admin_program_knowledge_step_path(knowledge.program.id, knowledge.id))
end
index do
column :id
column :knowledge
column :title
column "Actions" do |step|
link_to("Voir", admin_program_knowledge_step_path(step.knowledge.program, step.knowledge, step), :class => "member_link show_link") +\
link_to("Editer", edit_admin_program_knowledge_step_path(step.knowledge.program, step.knowledge, step), :class => "edit_knowledge member_link edit_link", :id => "knowledge_#{dom_id(knowledge)}") +\
link_to("Supprimer", admin_program_knowledge_step_path(step.knowledge.program, step.knowledge, step), :class => "member_link delete_link", :method => :delete, :confirm => "Delete?")
end
end
filter :id
filter :title
filter :subtitle
filter :stage_type
filter :order_by
filter :created_at
filter :updated_at
form :partial => "form"
end
And in app/views/admin/steps/_form.html.erb I must use the activeadmin formbuilder:
<%= semantic_form_for(resource, :url => admin_program_knowledge_steps_path(resource.knowledge.program, resource.knowledge), :builder => ActiveAdmin::FormBuilder) do |f|
f.inputs "Step" do
f.input :knowledge, :as => :hidden
f.form_buffers.last << f.template.content_tag(:li, f.template.content_tag(:label, "Knowledge")+f.template.content_tag(:p, f.object.knowledge.title))
f.input :title
f.input :order_by
end
f.buttons
end %>
Well... I'm stuck.
How to handle this nicely? Any clues appreciated...
Well, the solution is pretty simple...
https://github.com/josevalim/inherited_resources
ActiveAdmin.register Step do
controller do
nested_belongs_to :program, :knowledge
end
end

How to customize formtastic attribute :as=>:check_boxes

my problem is that I try to customize the formatastic view. But before I go into detail, I'll explain my model.
I have 2 objects with a n:m relation Shop and Category
Shop model looks like that:
has_many :shop_categories, :class_name => "ShopCategory", :foreign_key => "shop_id"
has_many :categories, :through => :shop_categories, :source => :categories
Category model looks like that:
has_many :shop_categories, :class_name => "ShopCategory", :foreign_key => "category_id"
has_many :shops, :through => :shop_categories, :source => :shops
And of course my m to n table looks like
belongs_to :shops, :class_name => "Shop", :foreign_key => "shop_id"
belongs_to :categories, :class_name => "Category", :foreign_key => "category_id"
validates :shop_id, :presence => true
validates :category_id, :presence => true
This works fine and the following command in my Shop view will list all elements from categories within checkboxes:
<%= f.input :categories, :as => :check_boxes, :id => 'shop_categories' %>
Here is my problem:
Within categories I have a name for the category and a picture. Now I want to display the picture next to the selectbox.
I also tryed to use <% f.fields_for :categories do |category| %> but rails wont go through all category elements.
Is there a way to handle all Category elements with automatic checked objects?
If you need more information, i will be glad to give all what you need to understand the problem.
Thank you for any hint.
Try overriding custom inputs:
# app/inputs/collection_check_boxes_input.rb
class CollectionCheckBoxesInput < SimpleForm::Inputs::CollectionCheckBoxesInput
# [...]
end