During a signup process I have a user model and Tenant model. Recently I added a serialized column to the Tenant model and I can update the this column fine. However when creating a new Tenant I have devise creating tenant through nested parameters and I get the following error:
ActiveRecord::SerializationTypeMismatch (Attribute was supposed to be a Hash, but was a String. -- "{}"): Important to note that I don't touch that column during the sign up process I have tried including the column on the sanitizer but it does the same. On the schema there is a default value which is '{}'. Below some of the code:
create_table "tenants", force: :cascade do |t|
t.string "tenant_name"
t.string "tenant_address"
t.string "tenant_city"
t.string "tenant_zip"
t.string "tenant_phone"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "authorized"
t.boolean "trial"
t.string "plan_id"
t.string "plan_name"
t.string "braintree_id"
t.string "subscription_id"
t.jsonb "preferences", default: "{}", null: false
t.string "tenant_state"
t.string "tenant_country"
t.index ["preferences"], name: "index_tenants_on_preferences", using: :gin
end
class Tenant < ApplicationRecord
has_many :users, :dependent => :delete_all
has_many :customers, :dependent => :delete_all
has_many :work_orders, :dependent => :delete_all
has_many :vehicles, :dependent => :delete_all
has_many :suppliers, :dependent => :delete_all
end
serialize :preferences, Hash
store_accessor :preferences, :state_tax, :mun_tax, :welcome_sms, :estimate_sms, :completed_sms, :disclaimer
Here is part of my User controller:
class Users::RegistrationsController < Devise::RegistrationsController
before_action :configure_sign_up_params, only: [:create]
# before_action :configure_account_update_params, only: [:update]
# GET /resource/sign_up
def new
build_resource({})
self.resource.tenant = Tenant.new
respond_with self.resource
end
# POST /resource
def create
super
if #user.save
#result = Braintree::Customer.create(
:first_name => #user.name,
:last_name => #user.lastname,
:company => #user.tenant.tenant_name,
:email => #user.email,
:phone => #user.phone
)
if #result.success?
#user.tenant.set_braintree_id(#result.customer.id)
flash[:notice] = 'Thanks you! and Welcome to Autokick.tech enjoy your free 30 days!'
else
flash[:notice] = #result.errors
end
end
end
t.jsonb "preferences", default: "{}", null: false
The default is a string "{}" like the error says.
Change it to default: {} without the quotes.
Related
I'm trying to create an association between two models in my Rails app (User and Coin) where Coin belongs_to User and User has_many Coins. When I add the belongs_to association in the Coin model, I am no longer able to edit or create Coin pages. Why would it do this? As soon as I remove the association, I'm able to create/edit again. Also, the corresponding has_many association on the User page does not have the same effect. I'd appreciate any help in understanding what is happening here and how I can properly make this association. Thanks.
User.rb
class User < ApplicationRecord
acts_as_votable
has_many :questions, dependent: :destroy
has_many :events, dependent: :destroy
has_many :links, dependent: :destroy
has_many :posts, dependent: :destroy
has_many :moderated_coins, class_name: "Coin"
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable,
:validatable, authentication_keys: [:login]
validates :username, presence: :true, uniqueness: { case_sensitive: false }
validates_format_of :username, with: /^[a-zA-Z0-9_\.]*$/, :multiline => true
validate :validate_username
def validate_username
if User.where(email: username).exists?
errors.add(:username, :invalid)
end
end
def login=(login)
#login = login
end
def login
#login || self.username || self.email
end
def self.find_for_database_authentication(warden_conditions)
conditions = warden_conditions.dup
if login = conditions.delete(:login)
where(conditions.to_h).where(["lower(username) = :value OR lower(email) = :value", { :value => login.downcase }]).first
elsif conditions.has_key?(:username) || conditions.has_key?(:email)
where(conditions.to_h).first
end
end
end
Coin.rb
class Coin < ApplicationRecord
validates :currency_name, presence: true
has_many :questions, dependent: :destroy
has_many :events, dependent: :destroy
has_many :links, dependent: :destroy
mount_uploader :picture, PictureUploader
has_and_belongs_to_many :genres
# belongs_to :moderator, class_name: "User". <--- * The problem is here
validate :picture_size
private
def picture_size
if picture.size > 5.megabytes
errors.add(:picture, "Picture must be smalled than 5MB.")
end
end
end
coins_controller.rb
class CoinsController < ApplicationController
load_and_authorize_resource param_method: :question_params
before_action :find_coin, only: [:edit, :update, :destroy ]
before_action :authenticate_user!, except: [:index, :create, :show]
def index
#search = Coin.ransack(params[:q])
#coins = #search.result(distinct: true)
end
def new
#coin = Coin.new
end
def create
#coin = Coin.new(coin_params)
if #coin.save
flash[:success] = "Coin created"
redirect_to #coin
else
render 'new'
end
end
def show
#coin = Coin.find(params[:id])
end
def edit
authorize! :update, #coin
end
def update
if #coin.update(coin_params)
redirect_to #coin
else
render 'edit'
end
end
def destroy
Coin.find(params[:id]).destroy
redirect_to coins_url
end
private
def coin_params
params.require(:coin).permit( :currency_name, :currency_abbrev, :moderator_id, :accepted, :picture, :question1, :question2, :question3, :question4, genre_ids:[])
end
def find_coin
#coin = Coin.find(params[:id])
end
end
user_controller.rb
class UsersController < ApplicationController
before_action :authenticate_user!
def show
#user = User.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #user }
end
end
end
schema.rb
create_table "coins", force: :cascade do |t|
t.string "link_name"
t.string "currency_name"
t.string "currency_abbrev"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "permalink"
t.boolean "accepted", default: false
t.datetime "accepted_at"
t.string "genre"
t.integer "genre_id"
t.integer "moderator_id"
end
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "username"
t.string "wallet"
t.boolean "admin", default: false
t.boolean "moderator", default: false
t.decimal "currentbalance", precision: 8, scale: 2
t.decimal "payout_to_date", precision: 8, scale: 2
t.text "bio"
t.string "link1"
t.string "link2"
t.string "link3"
t.string "link4"
t.string "link5"
t.string "name"
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
t.index ["username"], name: "index_users_on_username", unique: true
end
Use:
belongs_to :moderator, class_name: "User", optional: true
In rails 5, belongs_to enforces existence of the associated record by default. You need to use optional: true in order to allow moderator_id to be nil.
I have 3 models - User, Shipment and Friendship. User can be friends with another user via Friendship-model. User also can create Shipments and can add a Friend-User to it. There is address-attribute in User and Shipment models. I need to give User a possibility to fill that address field in 2 ways at the same form:
By filling the address field manually.
By choosing from select-list a Friend of that User - so the Friends
address-attribute transfers and fills the Shipments adress-attribute
(like ctrl-c/ctrl-v) and User can Submit the form.
I can guess, that AJAX is needed to refresh the content without refreshing the page.
Shipment model:
class Shipment < ActiveRecord::Base
belongs_to :user
belongs_to :friendship
validates :image, presence: true
validates :user_id, presence: true
end
Shipments controller:
class ShipmentsController < ApplicationController
helper_method :shipment, :user
before_action :set_shipment, only: [:show]
before_action :authenticate_user!
before_action :require_same_user, only: [:show]
def index
#shipments = Shipment.all
end
def new
#shipment = Shipment.new
end
def create
#shipment = Shipment.new(shipment_params)
#shipment.user = current_user
if #shipment.save
flash[:success] = "Shipment etc."
redirect_to shipment_path(#shipment)
else
render 'new'
end
end
def show
#shipment = Shipment.find(params[:id])
end
private
def user
#user = current_user
end
def shipment
#shipment = user.shipments.new
end
def shipment_params
params.require(:shipment).permit(:name, :kg, :length, :width, :height,
:adress, :image, :user_id, :friend_id)
end
def set_shipment
#shipment = Shipment.find(params[:id])
end
def require_same_user
if current_user != #shipment.user
flash[:alert] = "Restricted/"
redirect_to root_path
end
end
end
User model:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :shipments, dependent: :destroy
has_many :friendships
has_many :friends, through: :friendships
has_many :inverse_friendships, :class_name => 'Friendship',
:foreign_key => 'friend_id'
has_many :inverse_friends, :through => :inverse_friendships, :source => :user
end
Users controller (the User itself is created by Devise)
class UsersController < ApplicationController
before_action :authenticate_user!
def show
#user = User.find(params[:id])
end
def my_friends
#friendships = current_user.friends
end
def search
#users = User.search(params[:search_param])
if #users
#users = current_user.except_current_user(#users)
render partial: 'friends/lookup'
else
render status: :not_found, nothing: true
end
end
private
def require_same_user
if current_user != set_user
flash[:alert] = "Restricted."
redirect_to root_path
end
end
def set_user
#user = User.find(params[:id])
end
end
Friendship model:
class Friendship < ActiveRecord::Base
belongs_to :user
belongs_to :friend, class_name: 'User'
has_many :shipments
end
Friendships controller:
class FriendshipsController < ApplicationController
def index
#friendships = Friendship.all
end
def create
#friendship = current_user.friendships.build(:friend_id => params[:friend_id])
if #friendship.save
flash[:success] = "Added to friends."
redirect_to my_friends_path
else
flash[:alert] = "Impossible to add as a friend."
redirect_to my_friends_path
end
end
def destroy
#friendship = current_user.friendships.find_by(friend_id: params[:id])
#friendship.destroy
flash[:notice] = "Unfriended."
redirect_to my_friends_path
end
private
def name
#name = friend_id.name
end
end
Schema:
create_table "friendships", force: :cascade do |t|
t.integer "user_id"
t.integer "friend_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "shipments", force: :cascade do |t|
t.string "name"
t.integer "length"
t.integer "width"
t.text "adress"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "image_file_name"
t.string "image_content_type"
t.integer "image_file_size"
t.datetime "image_updated_at"
t.integer "height"
t.integer "kg"
end
add_index "shipments", ["user_id"], name: "index_shipments_on_user_id"
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "name"
t.integer "phone", limit: 30
t.string "username"
end
add_index "users", ["email"], name: "index_users_on_email", unique: true
add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
Shipment form view (new):
<%= form_for(shipment, html: { multipart: true }) do |f| %>
<p>Choose a friend from your friendlist or fill the address field manually:</p>
<%= f.select :friend_id, user.friendships.map{ |friendship|
[friendship.friend.name, friendship.id] } %>
<%= f.text_field :adress, placeholder: "Address and index" %>
<%= f.submit "Submit", class: "button" %>
<% end %>
With ActiveRecord::Base, you could use eager loading and nested form to solve your problem.
Eager load the object related to the main object and use nested form to display the related object.
I am new to Ruby and Rails, trying to fix an error I constantly get. Not sure how to fix it. Please help..
Route.rb
namespace :my do
namespace :account do
resource :details, :only => [:show, :update]
resources :addresses
end
end
AddressesController
class My::Account::AddressesController < MyController
def index
#addresses = current_user.addresses
end
def new
#address = current_user.addresses.new
end
....
end
Error - undefined method `addresses'
NoMethodError in My::Account::AddressesController#index
undefined method `addresses' for #<User:0x007fc955029380>
Schema.rb for customer addresses and users
create_table "customer_addresses", force: true do |t|
t.integer "customer_id"
t.string "name"
t.string "line_1"
t.string "line_2"
t.string "line_3"
t.string "line_4"
t.string "line_5"
t.string "postcode"
t.string "phone"
t.datetime "deleted_at"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "customer_addresses", ["customer_id"], name: "index_customer_addresses_on_customer_id"
create_table "users", force: true do |t|
t.string "first_name"
t.string "last_name"
t.string "email"
t.string "password_digest"
t.datetime "created_at"
t.datetime "updated_at"
t.string "password_reset_token"
t.datetime "password_reset_token_at"
end
add_index "users", ["email"], name: "index_users_on_email", unique: true
User.rb
class User < ActiveRecord::Base
validates :email, :presence => true, :uniqueness => true
has_secure_password
...
end
Customer.rb
class Customer < User
has_many :addresses
def self.register(attributes)
customer = create!(attributes)
return customer
end
def full_name
"#{first_name} #{last_name}"
end
end
Address.rb
class Customer::Address < ActiveRecord::Base
belongs_to :customer
self.table_name = 'customer_addresses'
default_scope { where(:deleted_at => nil) }
validates :line_1, :postcode, :phone, :presence => true
end
You need to define the relationship on both models.
class User < ActiveRedord::Base
has_many :addresses, class_name: 'CustomerAddress', foreign_key: 'customer_id'
end
class Address < ActiveRecord::Base
belongs_to :user, foreign_key: 'customer_id'
end
Add a #current_customer method to your ApplicationController that return a Customer-instance instead of a User-instance:
class ApplicationController
#…
private
def current_customer
current_user && Customer.find_by_id(current_user.id)
end
end
then change your code like this:
class My::Account::AddressesController < MyController
def index
#addresses = current_customer.addresses
end
def new
#address = current_customer.addresses.new
end
#…
end
I have the following models:
class Office < ActiveRecord::Base
belongs_to :city
belongs_to :company
end
class Company < ActiveRecord::Base
has_one :acquirer
has_many :offices
has_many :cities, through: :offices
end
class City < ActiveRecord::Base
has_many :offices
end
My Offices controller is set up this way:
class OfficesController < ApplicationController
before_action :set_office, only: [:show, :edit, :update, :destroy]
respond_to :html, :json
def index
respond_with(#offices = Office.all(:include => [:company, :city]))
end
...
And my schema.rb:
create_table "cities", id: false, force: true do |t|
t.string "name", null: false
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "companies", id: false, force: true do |t|
t.string "name", null: false
t.string "website"
t.datetime "created_at"
t.datetime "updated_at"
t.string "acquirer_id"
end
create_table "offices", force: true do |t|
t.boolean "headquarters"
t.string "city_id"
t.string "company_id"
t.datetime "created_at"
t.datetime "updated_at"
end
I'm not really sure what's wrong.
All I really want is to show the company_id and city_id columns. I have an Acquisitions Controller that shows these columns in JSON even without the respond_with method. So I don't understand why it works by default in that case and not in this. I'm using Rails 4.0.0 with Ruby 2.0.0.
You should always use integer for defining foreign keys.
Coming back to your question, you can use
respond_with Office.all(:include => [:company, :city])).as_json(:include => [:company,:city])
However, if you have complex JSON responses I would suggest you to look at RABL or jbuilder
I got it to work. Leaving controller as it is, I changed index.json.jbuilder from:
json.array!(#offices) do |office|
json.extract! office, :headquarters
json.url office_url(office, format: :json)
end
to:
json.array!(#offices) do |office|
json.extract! office, :headquarters, :company_id, :city_id
json.url office_url(office, format: :json)
end
Not sure what you guys think of this fix?
I'm a complete rails newbie, so forgive me if this is trivial.
I have an Inventory model that either belongs_to a Store or a Traveling Party:
class Inventory < ActiveRecord::Base
belongs_to :trader, :polymorphic => true
end
class Store < ActiveRecord::Base
has_one :inventory, :as => :trader, :dependent => :destroy
end
class TravelingParty < ActiveRecord::Base
has_many :travelers, :dependent => :destroy
has_one :inventory, :as => :trader, :dependent => :destroy
validates_presence_of :speed, :ration, :position
accepts_nested_attributes_for :travelers, :reject_if => :reject_traveler, :allow_destroy => true
accepts_nested_attributes_for :inventory, :allow_destroy => true
def reject_traveler(attributes)
attributes['profession'].blank? and attributes['name'].blank?
end
end
I created a form that, when submitted, creates a Traveling Party and a number of Travelers. Now I'd like the form to also create an Inventory and initialize all the variables to 0. I know the following doesn't address variable initialization, but it doesn't even seem to put a row of null values into the Inventory database table.
class TravelingPartiesController < ApplicationController
def new
#traveling_party = TravelingParty.new
5.times do
traveler = #traveling_party.travelers.build
end
#inventory = #traveling_party.inventory.create
end
def create
#traveling_party = TravelingParty.new(params[:traveling_party])
if #traveling_party.save
flash[:notice] = "Successfully created traveling party and travelers."
redirect_to '/store/'
else
flash[:error] = "Please specify a leader."
redirect_to '/new/'
end
end
def index
end
end
For good measure, here is what the database schema looks like:
ActiveRecord::Schema.define(:version => 20111018224808) do
create_table "inventories", :force => true do |t|
t.integer "ox"
t.integer "food"
t.integer "clothing"
t.integer "ammunition"
t.integer "money"
t.integer "axle"
t.integer "wheel"
t.integer "tongue"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "trader_id"
end
create_table "stores", :force => true do |t|
t.string "name"
t.integer "location"
t.integer "priceScale"
t.datetime "created_at"
t.datetime "updated_at"
end
# Could not dump table "travelers" because of following StandardError
# Unknown type 'relations' for column 'traveling_party_id'
create_table "traveling_parties", :force => true do |t|
t.integer "speed"
t.integer "ration"
t.integer "position"
t.datetime "created_at"
t.datetime "updated_at"
end
end
Is there a reason the inventory database table isn't being affected at all? And once that works, what would be the best way to initialize a traveling_party.inventory to have all 0s? (i.e., values for ox, food, clothing, etc).
This may because your inventories table does not include a 'trader_type'. This is required for polymorphic associations.
create_table "inventories", :force => true do |t|
t.integer "trader_id"
t.string "trader_type"
end
Edit:
To set all the values initially to 0, the best way would be to put a default value onto the fields in the table. (If you want it to always be initialized to 0 if there is no other option, otherwise they will default to nil)
I believe you can create a migration with
change_table(:inventories) do |t|
t.change :ox, :integer, :default => 0
end