create ruby object if all attributes are defined - ruby

I have a hash that is defining some variables as such:
event_details = {
:title => title(event),
:desc => desc(event),
:url => url(event),
:datetime => datetime(event),
:address => address,
:lat => coords["lat"],
:lng => coords["lng"]
}
and I want to create an "event" object
event = Event.new(event_details)
Now I only want to create the object event if and only if all the attributes are defined and not nil, for example, the lat and lng variables are sometimes nil, and I do not want to create that event object.
I was thinking about having a rescue clause in my event class, but I am not sure how I only create the instance of event, and validate the presence of each attribute. any tips would be greatly appreciated

I suggest:
keys = [:title, :desc, :url, :datetime, :address, :lat, :lng]
event = Event.new(event_details) unless
(keys-event_details.keys].any? || event_details.values.any?(&:nil?)
or
if (keys-event_details.keys].any? || event_details.values.any?(&:nil?)
<..action..>
else
event = Event.new(event_details)
end

You can do:
class Event
attr_accessor :title, :desc
RequiredKeys = [:title, :desc]
def initialize(hash = {})
missing_keys = RequiredKeys - hash.keys
raise "Required keys missing: #{missing_keys.join(', ')}" unless missing_keys.empty?
hash.each do |key, value|
public_send("#{key}=", value);
end
end
end

Related

How can i return some message if before_validation method is false by Sinatra

I want to return some message that tell the user their password is not match with the confirm password field.
This is my code
post '/signup' do
user = User.new(params[:user])
if user.create_user
"#{user.list}"
else
"password not match"
end
end
require 'bcrypt'
class User < ActiveRecord::Base
include BCrypt
# This is Sinatra! Remember to create a migration!
attr_accessor :password
validates :username, presence: true, uniqueness: true
validates :email, :format => { :with => /\A[\w+\-.]+#[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i ,
:message => "Email wrong format" }
validates :email, uniqueness: true,presence: true
validates :encrypted_password, presence: true
validates :encrypted_password, length: { minimum: 6 }
before_validation :checking_confirm_password
def initialize(signup = {})
##signup = signup
super()
#username = signup["username"]
#email = signup["email"]
#password = signup["password"]
#confirm_password = signup["confirm_password"]
end
def create_user
p_p = Password.create(#password)
p_h ||= Password.new(p_p)
user_hash = {:username => #username,:email => #email, :encrypted_password => p_h}
return User.create(user_hash)
end
def list
User.all
end
private
def checking_confirm_password
if #password != #confirm_password
return false
end
end
end
So how can i specific the message that will send back to the user,
if their password is not match or the validation fail before create the dada?
Thanks!
The validations populate #user.errors with all validation errors with all validation errors by field, so you can easily return all validation errors at once like so:
post '/signup' do
user = User.new(params[:user])
if user.create_user
"#{user.list}"
else
user.errors.full_messages
end
end
Have a look at: http://edgeguides.rubyonrails.org/active_record_validations.html#working-with-validation-errors

How To Create A Search Function - Ruby On Rails

Im trying to make a search function for an app. I have a service object called searches and all search logic is in this object.
When the search form is submitted with an about query #results in Search Controller is the following array.
=> [
[0] [],
[1] [
[0] About {
:id => 2,
:title => "About",
:slug => "about",
:meta_description => "",
:meta_keywords => "",
:content => "<p>Lorem ipsum about</p>",
:position => 1,
:published => true,
:on_menu => true,
:created_at => Wed, 13 Jan 2016 00:44:08 UTC +00:00,
:updated_at => Fri, 15 Jan 2016 04:05:52 UTC +00:00
}
],
[2] [],
[3] []
]
Here is my Search object. See how the User attributes are different then the Pages and NewsArticle? Good. Now go look at the view.
class SearchService
attr_accessor :query
def initialize(query)
#query = query
end
# searchable_fields_by_model
CONDITIONS_BY_MODEL = {
Page => [:title, :content],
NewsArticle => [:title, :content]
User => [:first_name, :last_name]
}
def raw_results
ActiveRecord::Base.transaction do
CONDITIONS_BY_MODEL.flat_map do |model, fields|
Array(fields).map do |field|
model.where(["#{field} LIKE ?", "%#{query}%"])
end
end
end
end
end
Below is my view. Currently the view will only display content and title. But my User has first_name and last_name therefore it won't display. I need a way to display the results of all Models and it needs to be clean so if i add another model to search with entirely different attributes it will still display. How would you do this?
.page-header
%h1 Search#index
- #results.each do |results|
- results.each do |r|
%p= r.title
%p= r.content
Search controller
class SearchController < ApplicationController
def index
#results = SearchService.new(params[:q]).results
end
end
How to fix this.
class SearchController < ApplicationController
def index
#results = SearchService.new(params[:q]).results
#page_results = []
#news_article_results = []
#category_results = []
#results.each do |resource|
if resource.class == Page
#page_results << resource
elsif resource.class == NewsArticle
#news_article_results << resource
elsif resource.class == Category
#category_results << resource
else
end
end
end
end
class SearchService
MIN_QUERY_LENGTH = 3
attr_accessor :query
def initialize(query)
raise ArgumentError if query.length < MIN_QUERY_LENGTH
#query = query
end
# searchable_fields_by_model
CONDITIONS_BY_MODEL = {
Page => [:title, :content],
NewsArticle => [:title, :content],
Category => :name,
}
def results
#results ||= ActiveRecord::Base.transaction do
CONDITIONS_BY_MODEL.flat_map do |model, fields|
Array(fields).flat_map do |field|
model.where(["#{field} LIKE ?", "%#{query}%"])
end
end
end
end
end
Here is my view
.page-header
%h1 Search Results
%h2= "For #{params[:q]}"
%br
%h2 Pages
- #page_results.each do |resource|
%p= link_to "#{resource.title}", page_path(resource.slug)
= resource.content.html_safe
%h2 News Article
- #news_article_results.each do |resource|
%p= link_to "#{resource.title}", news_article_path(resource.slug)
= resource.content.html_safe
%h2 Category
- #category_results.each do |resource|
%p= link_to "#{resource.name}", category_path(resource.slug)

Mongo object's id as uploadings dirname in CarrierWave

I'm using Sinatra with Mongoid and CarrierWave. I need to store document's attachments in /public/attachments/DOCUMENTS_ID.
Model of Mongo document:
class Dcmnt
include Mongoid::Document
store_in collection: 'dcmnts'
field :published, type: Boolean
field :name, type: String
field :description, type: String
field :additional, type: String
field :created_at, type: Date
mount_uploader :attachment, Uploader, type: String
end
And action's code:
post '/admin/create' do
params.delete 'submit'
d = Dcmnt.new(
:published => params[:published],
:name => params[:name],
:description => params[:description],
:additional => params[:additional],
:created_at => Time.now
)
d.attachment = params[:photos]
d.save
end
When I'm setting up unloader like this:
class Uploader < CarrierWave::Uploader::Base
storage :file
def store_dir
'public/attachments/' + d.id
end
end
It doesn't works for some amzaing reason. Can you help me implement this feature?
Accessing models attributes in CarrierWave is provided via model key word
class Uploader < CarrierWave::Uploader::Base
storage :file
def store_dir
'attachments/' + model.id
end
end

parse json to object ruby

I looked into different resources and still get confused on how to parse a json format to a custom object, for example
class Resident
attr_accessor :phone, :addr
def initialize(phone, addr)
#phone = phone
#addr = addr
end
end
and JSON file
{
"Resident": [
{
"phone": "12345",
"addr": "xxxxx"
}, {
"phone": "12345",
"addr": "xxxxx"
}, {
"phone": "12345",
"addr": "xxxxx"
}
]
}
what's the correct way to parse the json file into a array of 3 Resident object?
Today i was looking for something that converts json to an object, and this works like a charm:
person = JSON.parse(json_string, object_class: OpenStruct)
This way you could do person.education.school or person[0].education.school if the response is an array
I'm leaving it here because might be useful for someone
The following code is more simple:
require 'json'
data = JSON.parse(json_data)
residents = data['Resident'].map { |rd| Resident.new(rd['phone'], rd['addr']) }
If you're using ActiveModel::Serializers::JSON you can just call from_json(json) and your object will be mapped with those values.
class Person
include ActiveModel::Serializers::JSON
attr_accessor :name, :age, :awesome
def attributes=(hash)
hash.each do |key, value|
send("#{key}=", value)
end
end
def attributes
instance_values
end
end
json = {name: 'bob', age: 22, awesome: true}.to_json
person = Person.new
person.from_json(json) # => #<Person:0x007fec5e7a0088 #age=22, #awesome=true, #name="bob">
person.name # => "bob"
person.age # => 22
person.awesome # => true
require 'json'
class Resident
attr_accessor :phone, :addr
def initialize(phone, addr)
#phone = phone
#addr = addr
end
end
s = '{"Resident":[{"phone":"12345","addr":"xxxxx"},{"phone":"12345","addr":"xxxxx"},{"phone":"12345","addr":"xxxxx"}]}'
j = JSON.parse(s)
objects = j['Resident'].inject([]) { |o,d| o << Resident.new( d['phone'], d['addr'] ) }
p objects[0].phone
"12345"
We recently released a Ruby library static_struct that solves the issue. Check it out.

Rails -- before_save not working?

I am following Michael Hartl's RoR tutorial, and it is covering the basics of password encryption. This is the User model as it currently stands:
class User < ActiveRecord::Base
attr_accessor :password
attr_accessible :name, :email,: password, :password_confirmation
email_regex = /^[A-Za-z0-9._+-]+#[A-Za-z0-9._-]+\.[A-Za-z0-9._-]+[A-Za-z]$/
#tests for valid email addresses.
validates :name, :presence => true,
:length => {:maximum => 50}
validates :email, :presence => true,
:format => {:with => email_regex},
:uniqueness => {:case_sensitive => false}
validates :password, :presence => true,
:length => {:maximum => 20, :minimum => 6},
:confirmation => true
before_save :encrypt_password
private
def encrypt_password
#encrypted_password = encrypt(password)
end
def encrypt(string)
string
end
end
(Obviously this isn't doing any encrypting because the encrypt method isn't really implemented but that's not my question)
I then wrote the following Spec (according to the tutorial):
require 'spec_helper'
describe User do
before(:each) do
#attr = { :name => "Example User", :email => "user#example.com",
:password => "abc123", :password_confirmation => "abc123"}
end
describe "password encryption" do
before(:each) do
#user = User.create!(#attr) # we are going to need a valid user in order
# for these tests to run.
end
it "should have an encrypted password attribute" do
#user.should respond_to(:encrypted_password)
end
it "should set the encrypted password upon user creation" do
#user.encrypted_password.should_not be_blank
end
end
end
The first of these tests passes, but since #user.encrypted_password is nil, the second test fails. But I don't understand why it's nil since the encrypt_password method should be being called by before_save. I know I must be missing something -- can someone please explain?
The encrypt_password method is incorrect, it should read:
def encrypt_password
self.encrypted_password = encrypt(password)
end
Note the use of self, which will properly set the attribute for the user object rather than creating an instance variable which is forgotten.
This is an old question and this is more of a comment but I don't have enough reputation to comment yet. Just wanted to link this question too as it goes into some solid detail about self.
Why isn't self always needed in ruby / rails / activerecord?

Resources