Iv'e gotten myself into a bit of a brain mess up these past two days. I'd like to be able to allow my users to create a campaign (same concept as surveys), it will allow them to request certain data they wish such as an email address. This will then allow the person completing the form to proceed and receive a download link after entering an email. The email entered should be stored for the person who created the campaign to view.
Iv'e taken the approach with nested forms, however I ran into the trouble of allowing emails to be entered and saved for the campaign creator to view.
Any help is appreciated, thanks.
campaign.rb model
class Campaign < ActiveRecord::Base
belongs_to :user
has_many :queries
accepts_nested_attributes_for :queries
end
query.rb model
class Query < ActiveRecord::Base
belongs_to :campaign
has_many :results
end
result.rb model
class Result < ActiveRecord::Base
attr_accessible :content, :email, :query_id
belongs_to :query
end
schema.rb
create_table "campaigns", force: :cascade do |t|
t.string "title"
t.text "description"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "campaigns", ["user_id"], name: "index_campaigns_on_user_id", using: :btree
create_table "queries", force: :cascade do |t|
t.integer "campaign_id"
t.text "content"
t.string "email"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "queries", ["campaign_id"], name: "index_queries_on_campaign_id", using: :btree
create_table "results", force: :cascade do |t|
t.integer "query_id"
t.text "content"
t.string "email"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "results", ["query_id"], name: "index_results_on_query_id", using: :btree
Part of campaign_controller.rb
private
# Use callbacks to share common setup or constraints between actions.
def set_campaign
#campaign = Campaign.find(params[:id])
end
def campaign_params
params.require(:campaign).permit(:title, :description, :queries_attributes)
end
def query_params
params.require(:query).permit(:content, :email, :campaign_id)
end
Related
I'm building a Rails app that has modals Outage, Service, Note and User.
Service has a boolean attribute is_down. By default, is_down is false. When the attribute is updated to true meaning the service goes down, an Outage should be created and a Note should also be created with User, automated.
This all happens in one update of the is_down attribute. If Service goes back up, the outage remains intact but now has an end_time.
Here is the 'story line`:
Service model:
class Service < ApplicationRecord
has_many :outages
has_many :notes
# This is where I'm confused
is_down
if self.is_down
Outage.create(start_time: Time.now, reason: nil)
Note.create(user_id: 1, entry: "Outage began at #{Time.now}", service_id: self.id)
end
end
end
Outage model:
class Outage < ApplicationRecord
belongs_to :service
has_many :notes
has_many :users, through: :notes
end
Note modal (a join table between Outage and User)
class Note < ApplicationRecord
belongs_to :outage
belongs_to :user
end
and User model:
class User < ApplicationRecord
has_many :notes
has_many :outages, through: :notes
end
Outage is more like a story line where during the outage, users can enter notes about what they've learned.
Here is the schema:
enable_extension "plpgsql"
create_table "notes", force: :cascade do |t|
t.text "entry"
t.boolean "is_public", default: true
t.bigint "outage_id"
t.bigint "user_id"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["outage_id"], name: "index_notes_on_outage_id"
t.index ["user_id"], name: "index_notes_on_user_id"
end
create_table "outages", force: :cascade do |t|
t.datetime "start_time"
t.datetime "end_time"
t.text "reason"
t.boolean "is_recurring", default: false
t.string "frequency", default: "None"
t.bigint "service_id"
t.index ["service_id"], name: "index_outages_on_service_id"
end
create_table "services", force: :cascade do |t|
t.string "name"
t.boolean "is_down", default: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "users", force: :cascade do |t|
t.string "username"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
add_foreign_key "notes", "outages"
add_foreign_key "notes", "users"
add_foreign_key "outages", "services"
end
Besides the initial question of automated creation on update of Service attribute, is_down, is this also a good way to go about implementing this?
I would suggest looking into the lifecycle callbacks for ActiveRecord. You can add an after_save callback to your Service class that checks to see if is_down has changed and then create or close an Outage
class Service < ApplicationRecord
has_many :outages
...
after_save :create_or_update_outage, if: is_down_changed?
...
private
def create_or_update_outage
if is_down
outages.create
else
outages.where(end_time: nil).last.update(end_time: Time.now)
end
end
I have 3 models, Challenge, Pun, User (managed by Clearance gem)
A User can create a Challenge. A Challenge contains many puns. A User can also create a Pun.
Everything is fine until I set a Pun to belong_to a User, then suddenly Puns are no longer saved.
class User < ApplicationRecord
include Clearance::User
has_many :challenges
has_many :puns
end
class Challenge < ApplicationRecord
has_many :puns, :dependent => :delete_all
belongs_to :user
end
class Pun < ApplicationRecord
belongs_to :challenge
belongs_to :user
end
In my PunController I have tried to establish the current_user id
def create
#pun = #challenge.puns.create(pun_params)
#pun.user_id = current_user.id if current_user
redirect_to #challenge
end
private
def set_challenge
#challenge = Challenge.find(params[:challenge_id])
end
def pun_params
params[:pun].permit(:pun_text,:user_id)
end
What am I doing wrong? I'm trying to keep it as simple as possible, but seems like Users don't want to be associated with more than one thing, particularly if nested. Is this a Clearance issue?
DB setup:
create_table "challenges", force: :cascade do |t|
t.text "title"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "start_time"
t.datetime "end_time"
t.bigint "user_id"
t.index ["user_id"], name: "index_challenges_on_user_id"
end
create_table "puns", force: :cascade do |t|
t.text "pun_text"
t.bigint "challenge_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.bigint "user_id"
t.index ["challenge_id"], name: "index_puns_on_challenge_id"
t.index ["user_id"], name: "index_puns_on_user_id"
end
create_table "users", force: :cascade do |t|
t.string "name"
t.string "email"
t.string "tagline"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "encrypted_password", limit: 128
t.string "confirmation_token", limit: 128
t.string "remember_token", limit: 128
t.index ["email"], name: "index_users_on_email"
t.index ["remember_token"], name: "index_users_on_remember_token"
end
Well in you currrent code you don't save user_id after setting it. And if you do not expect creation to fail you can do "create!".
So you can try:
def create
#challenge.puns.create!(pun_params.merge(user_id: current_user.id))
redirect_to #challenge
end
You can do this using simply hidden_field like in the form
<%= object.hidden_field :user_id, value: current_user.id %>
it won't work without user session because the relationship does not optional, and remove this line from the controller
#pun.user_id = current_user.id if current_user
and redirect
redirect_to #pun
it will work
I have three tables user, event, expected_event
My event are scrapped (every day with a rake task) from another
website
A user can create expected_event
So What I want to do is:
When a new event is found I want it to be compared to the Users expected_event...
If an event matches to any users expected_event then the users receive an email with the matching event (event.department, event.location_name)
I want to compare event.department to expected_event.department
I don't know how to do this...
expected_event.rb
class ExpectedEvent < ApplicationRecord
belongs_to :user
validates :department, presence: true
end
user.rb
class User < ApplicationRecord
has_many :expected_events
end
In the event model I have some methode to retreive city_name, location_nameand department
class Event < ApplicationRecord
def department
self.city[/\(.*?\)/].gsub(/[()]/, "").to_i
end
def city_name
self.city[/^[^\(]+/].rstrip!
end
def location_name
self.city[/\|(.*)/].gsub("|", "").strip
end
end
schema.rb
ActiveRecord::Schema.define(version: 20171210203403) do
create_table "events", force: :cascade do |t|
t.datetime "date"
t.string "city"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "nickname"
####
end
create_table "expected_events", force: :cascade do |t|
t.integer "department"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["user_id"], name: "index_expected_events_on_user_id"
end
end
EDIT
Is it correct if I add something like this to my user model?
has_many :matching_events, through: :events, source: :expected_events
When a new event is created, why don't you just query for ExpectedEvent records with matching paramters. Something like
event = Event.create(event_params)
if ExpectedEvent.where(city: event.city, ...).any?
#send email to user about the event
end
I am really stuck with dealing with querys from multiple tables in rails. If this is my controller:
def show
#user = User.find(params[:id])
#entry = Entry.joins(:event, :user).where(users: { id: '2' })
#event = Event.find(1)
end
and this is my models
class Entry < ApplicationRecord
belongs_to :user
belongs_to :event
class User < ApplicationRecord
has_many :entries, dependent: :destroy
class Event < ApplicationRecord
has_many :entries, dependent: :destroy
The query runs happily in rails console and returns the records but I don't know how to access specific units like entry.course in my embeded ruby.
Update
To clarify On a page I woulk like to output somthing for all the entries assosiated with the user logged in to the page.
eventName eventLocation eventDate course siCard
The schema:
ActiveRecord::Schema.define(version: 20171204183458) do
# These are extensions that must be enabled in order to support this
database
enable_extension "plpgsql"
create_table "entries", force: :cascade do |t|
t.string "course"
t.string "siCard"
t.bigint "user_id"
t.bigint "event_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["event_id"], name: "index_entries_on_event_id"
t.index ["user_id", "created_at"], name:
"index_entries_on_user_id_and_created_at"
t.index ["user_id"], name: "index_entries_on_user_id"
end
create_table "events", force: :cascade do |t|
t.string "eventName"
t.string "location"
t.date "eventDate"
t.text "description"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "eventLink"
t.string "eventCoordinates"
t.index ["eventName"], name: "index_events_on_eventName", unique: true
end
create_table "users", force: :cascade do |t|
t.string "name"
t.string "email"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "password_digest"
t.string "firstName"
t.string "remember_digest"
t.boolean "admin"
t.index ["email"], name: "index_users_on_email", unique: true
end
add_foreign_key "entries", "events"
add_foreign_key "entries", "users"
end
You can find it in 2 ways
1)
#entries = Entry.includes(:user,:event).where(user: { id: (params[:id]) })
2)
#entries = User.includes(entries: :event).find(params[:id]).entries
Than in the loop of entries you can access value course of particular entry
e.g.
#entries.each do |entry|
puts "#{entry.course}"
puts "#{entry.event&.name}"
end
Why are you using such a complicated query with joins? If you are looking for the Entrys of #user just use #user.entries. ActiveRecord knows from the has_many how to deal with that.
If you posted a simplified example, you should try to find your Entry with something like .where(user_id: user.id) as long as id is the foreign key. With that you don't need that join, too.
You could confine with one query using
eager_load
def show
#user = User.eager_load(:entries => [:event]).where(users: {id: params[:id]})
end
According to your models you can access entries though user.
#user.entries
#user.entries.map{|entry| entry.course}
Also you can access event (which belongs to entry) though entry.
#user.entries[1].event
#user.entries.map{|entry| entry.event}
In my app User can have many Companies and vice versa. In Accounts table id of User and id of its Company is stored.
I want to find all Users who belong to Companies, which belong to current_user. Let's assume that the current_user is like master User (not Admin, as that would be system Admin) of those companies.
How do I do this? My guess is to do it with Arel, but then how should it look in Model, Controller, View? Many thanks for any help. I'm on Rails 5.
models/user.rb
class User < ApplicationRecord
has_many :accounts, dependent: :destroy
has_many :companies, through: :accounts
models/account.rb
class Account < ApplicationRecord
belongs_to :company
belongs_to :user
accepts_nested_attributes_for :company, :user
models/company.rb
class Company < ApplicationRecord
has_many :accounts, dependent: :destroy
has_many :users, through: :accounts
accepts_nested_attributes_for :accounts, :users
My schema.rb looks like this:
create_table "accounts", force: :cascade do |t|
t.integer "company_id"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["company_id"], name: "index_accounts_on_company_id"
t.index ["user_id"], name: "index_accounts_on_user_id"
end
create_table "companies", force: :cascade do |t|
t.string "name"
t.string "legal_name"
t.string "reg_number"
t.string "address"
t.string "bank_acc"
t.string "description"
t.string "website"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "role", default: 0
t.integer "currency", default: 0
end
create_table "users", force: :cascade do |t|
t.string "name"
t.string "email"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "password_digest"
t.string "remember_digest"
t.boolean "admin", default: false
t.index ["email"], name: "index_users_on_email", unique: true
end
You can find current user's companies, and eager load users who belong to those companies
#companies = current_user.companies.includes(:users)
To list all users(may be in a view), loop through #companies and all its users
#companies.each do |company|
company.users.each do |user|
puts user.name
end
end
Or use map/collect to assign them to a variable.
#users = #companies.map(&:users).flatten