How to loop through all available arrays in Ruby code - ruby-on-rails

I am trying to create a dashboard where users (User model) who has clicked "attending" (which is a flaggable) to an event (Event model) which is connected to a Collection (Collection model) will be able to see what are the Events they are going for.
My question however, is simply how to loop through all possible arrays in order for me to get all the associated IDs for the Events that the User has clicked "attending".
So for example, in my home page I want to display all possible events that the user is attending:
user_statistics.html.erb
<div class="span3 events">
<h3>Events</h3>
<% if #events.empty? %>
<p>You are currently not attending any events.</p>
<% else %>
<p>You are attending: <b><%= pluralize(#events.count, "event") %></b></p>
<p>Event 1: <%= #event1_name %> on Date: <%= #event1.date %> at Time:<%= #event1.time %></p>
<% end %>
</div>
pages_controller.rb
def home
#title = "Title"
#user = current_user
if current_user
#post = Post.new
#feed_items = current_user.feed
#user_following = #user.following
#user_followers = #user.followers
#events = #user.flaggings.with_flag(:attending)
#event1 = Event.find(#events[0].flaggable_id)
#event1_name = Collection.find(#event1.collection_id).name
end
end
I have set the #event1 array to 0 to access the first flag for 'attending', and then get the flaggable_id so I have the id to call up the Collection.
My issue is that if I have several events, how do I go about looping through to all the arrays to ensure I can pull out all the Collections?
For the first User who has clicked "attending" for 2 events, this is the data:
in IRB, for User.first who is attending 2 events
User Load (0.3ms) SELECT "users".* FROM "users" LIMIT 1
MakeFlaggable::Flagging Load (0.4ms) SELECT "flaggings".* FROM "flaggings" WHERE
"flaggings"."flagger_id" = 1 AND "flaggings"."flagger_type" = 'User' AND
"flaggings"."flag" = 'attending'
[#<MakeFlaggable::Flagging id: 16, flaggable_type: "Event", flaggable_id: 3,
flagger_type: "User", flagger_id: 1, flag: "attending", created_at: "2012-02-20 09:26:36",
updated_at: "2012-02-20 09:26:36">, #<MakeFlaggable::Flagging id: 18, flaggable_type:
"Event", flaggable_id: 4, flagger_type: "User", flagger_id: 1, flag: "attending",
created_at: "2012-02-20 10:38:00", updated_at: "2012-02-20 10:38:00">]
You can see that the user has flagged 'attending' for 2 events, which are stored in arrays. Hence if I have 2 events, I would ideally want to loop through such that I have 2 arrays.
I find it quite confusing to implement this... any help would be much appreciated!
user.rb partial code
Class User < ActiveRecord::Base
attr_accessible :name, :email, :password, :password_confirmation, :user_bio,
:shop, :cover_photo, :avatar, :remote_image_url
has_secure_password
mount_uploader :cover_photo, ImageUploader
mount_uploader :avatar, ImageUploader
make_flagger
scope :shop, where(shop: true)
has_many :posts, dependent: :destroy
has_many :relationships, dependent: :destroy,
foreign_key: "follower_id"
has_many :reverse_relationships, dependent: :destroy,
foreign_key: "followed_id",
class_name: "Relationship"
has_many :following, through: :relationships, source: :followed
has_many :followers, through: :reverse_relationships, source: :follower
has_many :collections, dependent: :destroy
...
end
collections.rb partial code
class Collection < ActiveRecord::Base
attr_accessible :name, :description, :image, :remote_image_url
belongs_to :user
has_many :products, dependent: :destroy
has_many :events, dependent: :destroy
mount_uploader :image, ImageUploader
make_flaggable :like
...
end
events.rb partial code
class Event < ActiveRecord::Base
attr_accessible :date, :time, :description
belongs_to :collections
make_flaggable :attending
...
end

It's not 100% clear from your post, but it sounds like you need a named scope for Events where the attending flag is set and a has_many :through association to let the User have Events. With those two bits together, you could do something like:
User.first.events.attending
and AREL will take care of wiring that all up into a nice fast query for you.

Is flaggable a gem or have you written it as a polymorphic class?
This line is kind of nasty (sorry):
#events = #user.flaggings.with_flag(:attending)
The #events variable isn't holding events - it's holding a collection of flaggings. As a naming convention, this is obviously bad.
Also, this line is probably redundant if you've set up (or used a gem) for the flaggable polymorphic relationship:
#event1 = Event.find(#events[0].flaggable_id)
#This could be rewritten as:
#event1 = #events.first.flaggable
Or even nicer, combine the two previous lines into:
##events = #user.flaggings.with_flag(:attending)
##event1 = Event.find(#events[0].flaggable_id)
#becomes:
flaggings = #user.flaggings.with_flag(:attending)
#events = flaggings.map(&:flaggable)
#jxpx777 makes a very good point about named scope and hmt associations. This is probably the way forward. You could do something like this:
Class User < AR...
has_many :attendances, :class => 'Flagging', :conditions => {:flag => 'attending'} #You might need to add :type => "Event" if you use flaggings elsewhere...
has_many :events_attending, :class => 'Event', :through => :attendances, :source => :flaggable_id #You'll probably have to change a lot of these variable names - I can't see the rest of your source code...
That will then give you ability to just do:
#events = #user.events_attending.include(:collection)
Caveat - none of this code is tested - it's all off the top of my head, but it should point you in the right direction at least

Okay, I managed to solve my own problem albeit in a very dirty manner...
pages_controller.rb
def home
#title = "Simplifying and socializing online shopping - Ruuva"
#user = current_user
if current_user
#post = Post.new
#feed_items = current_user.feed
#user_following = #user.following
#user_followers = #user.followers
#events_attending = #user.flaggings.with_flag(:attending)
end
end
_user_statistics.html.erb partial
<h3>Events</h3>
<% if #events_attending.empty? %>
<p>You are currently not attending any events.</p>
<% else %>
<p>You are attending these events:</p>
<ol>
<% #events_attending.each do |e| %>
<% unless Event.find_by_id(e.flaggable_id).nil? %>
<li>
Collection: <%= link_to Collection.find(Event.find(e.flaggable_id).collection_id).name, event_path(e.flaggable_id) %>
<br>happening on: <%= Event.find(e.flaggable_id).date %> at <%= Event.find(e.flaggable_id).time %>
</li>
<% end %>
<% end %>
</ol>
<% end %>
I'm pretty sure the code is very bad, and I DO used several 'shortcuts' issues that will bite me back in the future... but an mvp is an mvp. Thanks guys for the help, though! :)

Related

Ransack: Two filters each work on their own but not together, PG::UndefinedTable error

In my Rails 7 app I have parks and users. Users can mark parks as favorite and visited. I want to allow the currently logged in user to filter parks based on both whether they've favorited the park, AND whether they've visited the park.
Currently, the filters work if I only include one of them. But if I add both filters to the form, I get the following error:
ActiveRecord::StatementInvalid in Parks#index
PG::UndefinedTable: ERROR: missing FROM-clause entry for table "favorited_users_parks"
LINE 1: ...rks"."id" AND NOT ("users"."id" NOT IN (44))) AND "favorited...
Note - this seems very very similar to the issues described here, but I'm using Rails 7 and in this thread they claim the issue was resolved after updating to Rails 6.1.0: https://github.com/activerecord-hackery/ransack/issues/1119
And this thread seems to have some solutions that worked for other people but I don't understand how to use joins and ransacks well enough to understand how it applies to my own code: https://github.com/activerecord-hackery/ransack/issues/542
Here is the code:
views/parks/filters.erb
<div class="form-group">
<%= f.label :favorited_users, 'Saved to favorites' %>
<%= f.check_box :favorited_users_id_in, { class: "form-check-input" }, current_user&.id %>
</div>
<div class="form-group">
<%= f.label :visited_users_id, 'Not yet visited' %>
<%= f.check_box :visited_users_id_not_in, { class: "form-check-input" }, current_user&.id %>
</div>
controllers/parks_controller.rb
class ParksController < ApplicationController
def index
#parks = #q.result(distinct: true).paginate(page:params[:page], :per_page => 24)
end
end
models/park.rb
class Park < ApplicationRecord
has_many :visits, dependent: :destroy
has_many :visited_users, through: :visits, source: :user
has_many :favorites, dependent: :destroy
has_many :favorited_users, through: :favorites, source: :user
end
models/user.rb
class User < ApplicationRecord
has_many :visits, dependent: :destroy
has_many :visited_parks, through: :visits, source: :park
has_many :favorites, dependent: :destroy
has_many :favorited_parks, through: :favorites, source: :park
end
models/favorite.rb
class Favorite < ApplicationRecord
belongs_to :user
belongs_to :park
end
models/visit.rb
class Visit < ApplicationRecord
belongs_to :user
belongs_to :park
end
I haven't looked too much into it, seems the issue is still present. Solution is just sitting in a comment:
https://github.com/activerecord-hackery/ransack/issues/542#issuecomment-245700688
def index
#q = Park.ransack(params[:q]) # this is just an object for the form
#parks = search(Park.all) # this is for actual search results
end
private
def search(collection)
results = collection
params[:q]&.each { |k, v| results = results.ransack(k => v).result }
results
end
Just changing the order of the inputs also works.
You might want to skip hidden input for checkboxes, otherwise you send 0 when unchecked:
# vvvvvvvvvvvvvvvvvvvvv
<%= f.check_box :favorited_users_id_in, { include_hidden: false, class: "form-check-input" }, current_user&.id %>
# or like this vvv
<%= f.check_box :favorited_users_id_in, { class: "form-check-input" }, current_user&.id, nil %>

How to create view partial for this notification

I've already setup a notification for whenever a user posts a review on a book, the book.user gets notified in this partial: views/notifications/_book.html.erb. At the same time, I trying to create notification to a user's followers for whenever the user they're following posts a new book. IMO, supposed to create a book partial to render the notification view but I already have a duplicate of the book partial to show reviews notifications. Now I don't know if I can still tweak in in the same file or create something else. I'm using https://github.com/rails-engine/notifications.
I've implemented following relationships which is working well on my app from this tutorial https://www.devwalks.com/lets-build-instagram-with-ruby-on-rails-part-6-follow-all-the-people/
This is what I've done so far with the codes
user.rb
has_many :books, dependent: :destroy
has_many :chapters, dependent: :destroy
has_many :reviews, dependent: :destroy
has_many :genres
has_many :ratings
review.rb
belongs_to :book
belongs_to :user
after_commit :create_notifications, on: :create
private
def create_notifications
Notification.create do |notification|
notification.notify_type = 'book'
notification.actor = self.user
notification.user = self.book.user
notification.target = self
notification.second_target = self.book
end
end
views/notifications/_book.html.erb
<div class=''>
<%= link_to notification.actor.username, main_app.profile_path(notification.actor.username) %> reviewed
<%= link_to notification.second_target.title, main_app.book_path(notification.second_target) %>
</div>
<div class=''>
<% unless notification.target.blank? %>
<div class="review-rating" data-score="<%= notification.target.rating %>"></div>
<%= notification.target.comment %>
<% end %>
</div>
book.rb
belongs_to :user
has_many :chapters, dependent: :destroy
after_commit :create_notifications, on: :create
def create_notifications
self.user.followers.each do |follower|
Notification.create(notify_type: 'book',
actor: self.user,
user: follower,
target: self,
second_target: self.book)
end
end
So, if I've to render in the same partial, how should I? or maybe I've to do it another way?
After lots of studying I got to know that a notify_type partial must be must be created which must also correspond to the book's notify_type.
i.e I created a
notify_type: "new_book"
Name of partial= '_new_book.html.erb'

Rails Invoicing App

I want to create an invoice in rails. Invoice can have items and each item will have quantity, tax & price. It's a typical invoice we see everyday.
In order to create an invoice what is the best approach.
What is the common model for invoice and items?
I know Items will be a separate model. But how can we have one view for invoice, which creates both the invoice and items added to it?
What I mean is, Inside a new invoice page, there will be list of the clients, and list of the items , But here i'm not sure how to make the association when i create invoice. Is there any good example that i can follow ?
Please I'd appreciate some Help. Or even just a walk through of the steps i need to follow in order to accomplish that...
Here's my basic ERD
Quite a broad question, here's what I'd do:
#app/models/invoice.rb
class Invoice < ActiveRecord::Base
belongs_to :user
has_many :line_items
has_many :items, through: :line_items
accepts_nested_attributes_for :line_items
end
#app/models/line_item.rb
class LineItem < ActiveRecord::Base
belongs_to :invoice
belongs_to :item
end
#app/models/item.rb
class Item < ActiveRecord::Base
belongs_to :company
has_many :line_items
has_many :invoices, through: :line_items
end
--
#app/models/user.rb
class User < ActiveRecord::Base
has_many :invoices
end
This will be the base level "invoice" association structure - your clients/users can be built on top of it.
Your routes etc can be as follows:
#config/routes.rb
resources :invoices
#app/controllers/invoices_controller.rb
class InvoicesController < ApplicationController
def new
#invoice = current_user.invoices.new
#invoice.line_items.build
end
def create
#invoice = current_user.invoices.new invoice_params
#invoice.save
end
end
Then your view will be something like this:
#app/views/invoices/new.html.erb
<%= form_for #invoice do |f| %>
<%= f.fields_for :line_items do |l| %>
<%= f.text_field :quantity %>
<%= f.collection_select :product_id, Product.all, :id, :name %>
<% end %>
<%= f.submit %>
<% end %>
This would create the corresponding #invoice, with which you'll be able to call as follows:
#user.invoices.first
Apart from this, I don't have anywhere enough specific information to help specifically
May I recommend using the payday gem? I have created invoice models in the past applications and I'll tell you what, it can get pretty tricky sometimes depending on the type of application you're building. But the reason I like using this gem besides the convenience factor is that it can also render your invoices as a customizable PDF.
It makes adding items to the invoice a breeze as well, for example from their GitHub page:
invoice = Payday::Invoice.new(:invoice_number => 12)
invoice.line_items << Payday::LineItem.new(:price => 20, :quantity => 5, :description => "Pants")
invoice.line_items << Payday::LineItem.new(:price => 10, :quantity => 3, :description => "Shirts")
invoice.line_items << Payday::LineItem.new(:price => 5, :quantity => 200, :description => "Hats")
invoice.render_pdf_to_file("/path/to_file.pdf")

Rails 3.2.8 & JQuery Tokeninput: Trying to add new records for has_many through relationship instead of replacing all current records in a form

I have a database of skills that relate to each other as prerequisites to each other. In an index of skills, I'd like to be able to search through other skills and add 1 or more as prerequisites. It's important to note that I ONLY want the user to be able to add prerequisites, not remove them, as that's taken care of through an up-down voting system. I'm using JQuery Tokeninput and actually have all of this working except for one thing: I can't figure out how to only add prerequisites, rather than replacing all the prerequisites for a particular skill on submit.
Models:
class Skill < ActiveRecord::Base
attr_accessible :skill_relationship_attributes, :prereq_tokens
has_many :skill_relationships
has_many :prereqs, :through => :skill_relationships
has_many :inverse_skill_relationships, :class_name => 'SkillRelationship', :foreign_key => "prereq_id"
has_many :inverse_prereqs, :through => :inverse_skill_relationships, :source => :skill
attr_reader :prereq_tokens
accepts_nested_attributes_for :skill_relationships, :allow_destroy => true
def prereq_tokens=(ids)
self.prereq_ids = ids.split(",")
end
end
class SkillRelationship < ActiveRecord::Base
attr_accessible :skill_id, :prereq_id, :skill_attributes, :prereq_attributes
belongs_to :skill
belongs_to :prereq, :class_name => 'Skill'
end
JQuery:
$('#skill_prereq_tokens').tokenInput('/skills.json',
{ theme:'facebook',
propertyToSearch:'title',
queryParam:'search',
preventDuplicates:'true'
});
View:
<%= simple_form_for skill do |f| %>
<%= f.input :prereq_tokens %>
<%= f.submit %>
<% end %>
I feel a bit silly for not getting this before, but I solved my problem by changing how prereq_tokens became prereq_ids in my Skill model.
I just changed this:
def prereq_tokens=(ids)
self.prereq_ids = ids.split(",")
end
to this:
def prereq_tokens=(ids)
self.prereq_ids += ids.split(",")
end
That's it. That little plus sign before the equals sign. I hope this helps anyone else who codes too long without a break!

Accepting nested attributes on a belongs_to association

I have a complex form for scheduling events. Here are the abbreviated associations:
class Event < ActiveRecord::Base
belongs_to :client
accepts_nested_attributes_for :client, :reject_if => lambda { |a| a[:name].blank? }
end
class Client < ActiveRecord::Base
has_many :events
has_many :questions, :dependent => :destroy
accepts_nested_attributes_for :questions, :reject_if => lambda { |a| a[:content].blank? }
end
The form is creating a new event and I have the following structure:
- form_for #event do |event_form|
%select=collection_select(client_options_for_select, :options, :group_name, :id, :name, #event.client_id)
- event_form.fields_for :client do |client|
= client.text_field :name
- client.fields_for :questions do |question|
= question.text_field :content
The client already exists and is chosen from a select menu. An observer renders the nested attributes form by setting the client variable in a controller action and then rendering the partial.
Here is the error I'm getting:
ActionView::TemplateError (wrong number of arguments (0 for 1)) on line #1 of app/views/proceedings/_questions.html.haml:
1: - event_form.fields_for :client do |client|
app/views/proceedings/_questions.html.haml:1:in `form'
app/views/proceedings/_questions.html.haml:1:in `_run_haml_app47views47events47_client_questions46html46haml_locals_client_questions_object'
haml (3.0.21) rails/./lib/haml/helpers/action_view_mods.rb:13:in `render'
app/controllers/proceedings_controller.rb:261:in `__instance_exec0'
app/controllers/proceedings_controller.rb:260:in `corp_client_questions'
app/controllers/proceedings_controller.rb:258:in `corp_client_questions'
I'm having problems (I think) with the belongs_to association between Event and Client. I don't know if Event can accept nested attributes of Client when the Event belongs_to the Client. I've always done it the other way around (Client accepts nested attributes of Event).
Any ideas? I can elaborate if you need more code or background. Thanks!
Update: Added controller code as requested.
def client_questions
if params[:client_id].blank?
render_no_client_questions
elsif #client = Client.find(params[:client_id]) and #client.is_unspecified?
render_no_client_questions
else
respond_to do |format|
format.js {
render :update do |page|
page[:client_questions].replace_html :partial => 'client_questions', :layout => false
end
}
end
end
end
try adding an instance of the fields_for object in the options... generally a symbol isn't enough when creating a new top-level form object... Try the following, but yes it is possible to accept nested attributes on a belongs_to.
<%= event_form.fields_for :client, #client do |client| %>
<%= client.text_field :name %>
<%= client.fields_for :questions, Question.new do |question| %>
<%= question.text_field :content %>
<% end %>
<% end %>
I had the same issue using accepts_nested_attributes_for :address on a belongs_to :address association for my Order object.
The field_for wasn't echoing nothing, but all it took was adding
#order.build_address()
and that made it work.
I think is because we are using the association sort of reversed from the usual, so you have to manually create the association.
In your controller method(s), you need to add:
#event.build_client
The form cannot display fields_for when it does not have a valid object. Most of the time, we use something like #event.client.build, but this will not work with a belongs_to association. That method is only valid with has_many and has_and_belongs_to_many.
Reference here.

Resources