How do I work with checkboxes with DataMapper and Sinatra? - ruby

I'm trying to make a simple room management service. The rooms have these properties:
class Room
include DataMapper::Resource
validates_is_unique :number
property :id, Serial
property :number, Integer
property :guest, String
property :status, Enum[ :free, :occupied ], :default => :free
end
Then I create a new room like this
post '/new' do
content_type :json
#room = Room.new :guest => params[:guest],
:number => params[:number],
:status => params[:status]
if #room.save
{ :number => #room.number, :guest => #room.guest, :status => #room.status }.to_json
end
end
through this haml form
%form#new_message{:action => '/new', :method => 'post'}
%p
%input{:type => "text", :id => "number", :name => "number"}
%input{:type => "text", :id => "guest", :name => "guest"}
%input{:type => "checkbox", :id => "status", :name => "status", :value => "occupied"}
%input{:type => "submit", :value => "post"}
When the box is checked the :status is "occupied" but when I leave it unchecked the object won't save. I thought it would work since it is defaulted to "free" but no...

For whatever stupid reason, checkboxes do not get submitted if they are not clicked. This means they are not in the hash that hits your app. When you say :status => params[:status] you are really saying :status => nil. Since you have set a value, it checks that against your enum, and nil is not in your enum, so it fails validations. (based on how you are using this, doesn't it seem like it should be a boolean called either "occupied" or "available" ?)
Anyway, you could either explicitly set it to free, or not set it at all, and let the default take care of it. That is what I opted for when checking it, by moving it into a mass assignment. The code I used is below.
require 'rubygems'
require 'sinatra'
require 'haml'
require 'dm-core'
require 'dm-validations'
require 'dm-types'
require 'dm-migrations'
require 'sqlite3'
configure do
class Room
include DataMapper::Resource
validates_uniqueness_of :number
property :id, Serial
property :number, Integer
property :guest, String
property :status, Enum[ :free, :occupied ], :default => :free
end
set :sessions , true
DataMapper::Logger.new($stdout, :debug)
DataMapper.setup( :default , "sqlite3://#{Dir.pwd}/development.sqlite3" )
DataMapper.finalize
DataMapper.auto_upgrade!
end
get '/' do
#rooms = Room.all
haml :index
end
post '/new' do
p params
#room = Room.new params[:room]
if #room.save
session[:flash] = "room reserved"
redirect '/'
else
session[:flash] = #room.errors.to_a
redirect '/new'
end
end
get '/new' do
haml :new
end
__END__
##layout
!!!
%html
#flash
= session[:flash].inspect
= yield
##new
%form#new_message{:action => '/new', :method => 'post' , :name => 'room' }
%p
%input{:type => "text", :id => "number", :name => "room[number]"}
%input{:type => "text", :id => "guest", :name => "room[guest]"}
%input{:type => "checkbox", :id => "status", :name => "room[status]", :value => "occupied"}
%input{:type => "submit", :value => "post"}
##index
%table
- #rooms.each do |room|
%tr
%td= room.number
%td= room.guest
%td= room.status

View the HTML source of your web-form! There should be a hidden field which sets the unchecked checkboxes to '0' as the default , in case nobody checks them...
see also:
http://railscasts.com/episodes/17-habtm-checkboxes?autoplay=true (towards the end)

Related

The provided regular expression is using multiline anchors (^ or $)? [duplicate]

This question already has answers here:
Regular expressions with validations in RoR 4
(5 answers)
Closed 8 years ago.
I'm getting an error:
The provided regular expression is using multiline anchors (^ or $). Did you mean to use \A and \z, or forgot to add the :multiline => true option?
when loading only one page in my Rails application.
It highlights the model it's using saying the error is:
class Associate < Locations::Associate
This is the model:
class Associate < Locations::Associate
# Returns an array of permissions which are valid at the associate level.
#
def self.associate_permissions
ASSOCIATE_PERMISSIONS
end
# Generates an array of permission values that can be used in the new or edit
# template.
#
def permission_list
my_permissions = (permissions || '').split(/,/)
list = []
Associate.associate_permissions.each do |value|
list << {:label => value[0], :value => value[1], :checked => my_permissions.include? (value[1])}
end
list
end
end
The controller:
class AssociatesController < ApplicationController
def index
#associates = Associate.paginate :order => 'code',
:page => params[:page], :per_page => 50
respond_to do |format|
format.html # index.html.erb
format.json { render json: #associates }
end
end
end
Can anyone tell me how to solve this error?
I am assuming that you are getting the error while rendering an index view.
Update the index action with
#associates = Associate.order('code').paginate(:page => params[:page], :per_page => 50)
instead of
#associates = Associate.paginate :order => 'code',
:page => params[:page], :per_page => 50
I figured out my issue. I have a gem that runs most of my models. in the gems model I have:
class Locations::Associate < Locations::Database
require 'digest'
attr_accessible :code, :email, :include_on_reports, :name,
:permissions, :phone, :writer
has_many :associate_branches, :dependent => :destroy
validates :code, :presence => true, :uniqueness => true,
:format => { with: /^[A-Z]{3}\d{4}$/, on: :create }
.....
end
I had to change the regex to:
validates :code, :presence => true, :uniqueness => true,
:format => { with: /\A[A-Z]{3}\d{4}\z/, on: :create }
thanks for all the help.

Rails - has_many build method not saving association

I'm having some trouble trying to make association works.
My models looks like:
advertise.rb
class Advertise < ActiveRecord::Base
belongs_to :user
belongs_to :country
has_many :targets
# has_many :hss, :through => :targets
validates :description, :presence => true
validates :url, :presence => true
validates :country_id, :presence => true
validates :kind, :presence => true
attr_accessible :description, :hits, :url, :active, :country_id, :kind
KINDS = {
'1' => 'Commoditie',
'2' => 'Service',
}
HS = {
'1' => 'Section',
'2' => 'Chapter',
'4' => 'Heading',
'5' => 'SubHeading 1',
'6' => 'SubHeading 2',
}
end
hs.rb
class Hs < ActiveRecord::Base
attr_accessible :code, :kind
has_many :targets
has_many :advertises, :through => :targets
end
target.rb
class Target < ActiveRecord::Base
attr_accessible :advertise_id, :hs_id
belongs_to :advertise
belongs_to :hs
end
advertises_controller.rb
def new
#advertise = Advertise.new
#countries = Country.all
end
def create
#advertise = current_user.advertises.build(params[:advertise])
if #advertise.save
flash[:notice] = 'Advertise created with successful'
redirect_to root_path
else
render :new
end
end
the form for creating a new Ad.
/advertises/new.html.haml
%table.table.table-striped
%tr
%td{:colspan => 2}= advertise.input :url, :required => true
%tr
%td{:colspan => 2}= advertise.input :description, :required => true, :as => :text, :input_html => { :cols => 50, :rows => 3 }
%tr
%td{:colspan => 2}= advertise.input :country_id, :collection => #countries, :as => :select, :label => 'Origin', :required => true
%tr
%td{:colspan => 2}= advertise.input :kind, :collection => Advertise::KINDS.map(&:reverse), :as => :select, :label => 'Type', :required => true
%tr
%td{:colspan => 2}= advertise.input_field :active, as: :boolean, inline_label: 'Active'
=fields_for :targets do |target|
%tr
%td= select_tag :hs_kind, options_for_select(Advertise::HS.map(&:reverse).insert(0,'') )
%td= target.select :hs_id, ''
The hash params:
[3] pry(#<AdvertisesController>)> params
=> {"utf8"=>"✓",
"authenticity_token"=>"fOdn4NYLg/4HXruWURZPf9DYVT4EQzbaTRTKZvX1ugY=",
"advertise"=>
{"url"=>"http://test.com",
"description"=>"test",
"country_id"=>"17",
"kind"=>"2",
"active"=>"1"},
"hs_kind"=>"2",
"targets"=>{"hs_id"=>"487"},
"commit"=>"Create Advertise",
"action"=>"create",
"controller"=>"advertises"}
The hash seems ok to me, but it creates only the Advertise, and not creates the target for the advertise associated.
Any though? Maybe a wrong model association.
Thanks in advance.
try changing
=fields_for :targets do |target|
to
= advertise.fields_for :targets do |target|
and add the following to advertise.rb
accepts_nested_attributes_for :targets
attr_accessible :targets_attributes # just add targets_attributes to attr_accessible
be warned that when you do this, advertise objects that has no targets will not show the fields for targets. you have to build a new target object associated to advertise in order to show the fields
# example: on the new action of the controller
#advertise = Advertise.new
#advertise.targets.build

Not null fields for ActiveAttr

I'm having issues enforcing a field to not be nil within ActiveAttr::Model.
Is there an elegant way of enforcing this constraint within the model instead of defining it in the controller? Or am I testing incorrectly for the scenario?
Model:
class Message
include ActiveAttr::Model
attribute :name, :presence => true, :allow_nil => false, :allow_blank => false
attribute :email, :presence => true
attribute :content, :presence => true
validates_format_of :email, :with => /^[-a-z0-9_+\.]+\#([-a-z0-9]+\.)+[a-z0-9]{2,4}$/i
validates_length_of :content, :maximum => 500
end
RSpec:
module MessageSpecHelper
def valid_message_attributes
{
:name => "Walter White",
:email => "walter#hailtheking.com",
:content => "I am the one who knocks"
}
end
end
it "should have error on name (alternate with nil)" do
#message.attributes = valid_message_attributes.except(:name => nil)
#message.should have(1).error_on(:name)
end

Creating a class and adding methods dynamically in Ruby

How do I go about creating a new class and adding a few methods to it without resorting to "eval"?
Here's what I'm trying to do; I'd like to transform this structure:
obj = [
{
:scope => 'account',
:fields => [
{ :title => 'title', :length => 64, :required => true },
{ :title => 'email', :length => 256, :required => true, :type => 'email' }
],
:before_save => Proc.new{
#do something here
},
},
{
:scope => 'product',
:fields => [
{ :title => 'title', :length => 64, :required => true },
{ :title => 'description', :length => 256, :required => true },
{ :title => 'cost', :required => true, :type => 'decimal' }
]
},
]
into this:
class Account
include DataMapper::Resource
property :id, Serial
property :title, String, :length => 64, :required => true
property :email, String, :length => 256, :required => true
def before_save
#do something here
end
end
...
Thanks!
As Andrew already said, there are different ways to create class dynamically. This could be one of them:
Say you start with one DM model (haven't used DM, taking the first one from the docs):
class Post
include DataMapper::Resource
property :id, Serial # An auto-increment integer key
property :title, String # A varchar type string, for short strings
property :body, Text # A text block, for longer string data.
property :created_at, DateTime # A DateTime, for any date you might like.
end
and you want to create it dynamically, from a metadata given in a hash of the form
{:ClassName => {:field1 => :Type1, :field2 => :Type2 ...}}
You could do:
require 'data_mapper'
models = {:Post => {
:id => :Serial,
:title => :String,
:body => :Text
}}
models.each do |name, fields|
klass = Class.new do
include DataMapper::Resource
fields.each do |field_name, field_type|
property(field_name, const_get(field_type))
end
end
Object.const_set(name, klass)
end
Key methods:
Class.new
Module#const_set
If you want to look at a real-world example, please consult the code in this library: https://github.com/apohllo/rod/blob/v0.7.x/lib/rod/model.rb#L410

Rails Active Record Validation condition base

class Material < ActiveRecord::Base
belongs_to :material_type
belongs_to :product_review
validates :url, :presence => true, :if => :url_presence?
validates :video, :presence => true, :if => :video_presence?
def url_presence?
if !self.title.blank? and self.material_type.title.eql? :url
true
end
end
def video_presence?
if !self.title.blank? and self.material_type.title.eql? :video
true
end
end
has_attached_file :video,
:url => "/system/video/:id/:attachment/:style/:basename.:extension",
:path => ":rails_root/public/system/video/:id/:attachment/:style/:basename.:extension",
:default_url => "/image/video.png"
end
I assumption if its finds Title fields fill and material_type is url than it perform validation for url field presence validation check, but it not helpful
I think you need to compare strings, not symbols.
validates :url, :presence => true, :if => :url_present?
validates :video, :presence => true, :if => :video_present?
def url_present?
self.title.present? and self.material_type.title == "url"
end
def video_present?
self.title.present? and self.material_type.title == "video"
end

Resources