How to save received string parameters in array field? - ruby

How to extract and save array from string parameter? I'm trying convert string beafore_create but this doesn't work. When I comment before_create :waypoints Mongoid throw error:
Parameters: {
"utf8"=>"✓",
"authenticity_token"=>"nehoT1fnza/ZW4XB4v27uZsfFjjOu/ucIhzMmMKgWPo=",
"trip"=>{
"title"=>"test",
"description"=>"test",
"waypoints"=>"[[52.40637,16.92517],[52.40601,16.925040000000003],[52.405750000000005,16.92493],[52.40514,16.92463],[52.404320000000006,16.924200000000003]]"
}
}
Completed 500 Internal Server Error in 1ms
Mongoid::Errors::InvalidType (Field was defined as a(n) Array, but received a String with the value "[[52.40637,16.92517],[52.40601,16.925040000000003],[52.405750000000005,16.92493],[52.40514,16.92463],[52.404320000000006,16.924200000000003]]".):
EDIT Thanks for help, now it work but I don't know whether following approach is good. I remove before_create and change parameter name from waypoints to waypoints_s and def waypoints to def waypoints_s:
#Parameters:
#"waypoints"=>"[[52.40637,16.92517],[52.40601,16.925040000000003],[52.405750000000005,16.92493],[52.40514,16.92463],[52.404320000000006,16.924200000000003]]"
"waypoints_s"=>"[[52.40637,16.92517],[52.40601,16.925040000000003],[52.405750000000005,16.92493],[52.40514,16.92463],[52.404320000000006,16.924200000000003]]"
class Trip
include Mongoid::Document
field :title, :type => String
field :description, :type => String
field :waypoints, :type => Array
#before_create :waypoints
#def waypoints=(arg)
def waypoints_s=(arg)
if (arg.is_a? Array)
##waypoints = arg
self.waypoints = arg
elsif (arg.is_a? String)
##waypoints = arg.split(',')
self.waypoints = JSON.parse(arg)
else
return false
end
end
end
class TripsController < ApplicationController
def create
#trip = Trip.create(params[:trip])
#trip.save
end
end

Parse the string as a JSON object:
require 'json'
waypoints = "[[52.40637,16.92517],[52.40601,16.925040000000003],[52.405750000000005,16.92493],[52.40514,16.92463],[52.404320000000006,16.924200000000003]]"
JSON.parse(waypoints)
=> [[52.40637, 16.92517], [52.40601, 16.925040000000003], [52.405750000000005, 16.92493], [52.40514, 16.92463], [52.404320000000006, 16.924200000000003]]

You need to use serialize http://api.rubyonrails.org/classes/ActiveRecord/Base.html#method-c-serialize
This method serialize your object to database by YAML format (let's say just text with some format).
class Trip < ActiveRecord::Base
serialize :waypoints
end
trip = Trip.create( :waypoints => [[52.40637,16.92517],[52.40601,16.925040000000003],[52.405750000000005,16.92493],[52.40514,16.92463],[52.404320000000006,16.924200000000003]])
Trip.find(trip.id).waypoints # => [[52.40637,16.92517],[52.40601,16.925040000000003],[52.405750000000005,16.92493],[52.40514,16.92463],[52.404320000000006,16.924200000000003]]

Related

bad char after creating a Database from csv

I am trying to create a database using mongoid but it fails to find the create method. I am trying to create 2 databases based on csv files:
extract_data class:
class ExtractData
include Mongoid::Document
include Mongoid::Timestamps
def self.create_all_databases
#cbsa2msa = DbForCsv.import!('./share/private/csv/cbsa_to_msa.csv')
#zip2cbsa = DbForCsv.import!('./share/private/csv/zip_to_cbsa.csv')
end
def self.show_all_database
ap #cbsa2msa.all.to_a
ap #zip2cbsa.all.to_a
end
end
the class DbForCSV works as below:
class DbForCsv
include Mongoid::Document
include Mongoid::Timestamps
include Mongoid::Attributes::Dynamic
def self.import!(file_path)
columns = []
instances = []
CSV.foreach(file_path, encoding: 'iso-8859-1:UTF-8') do |row|
if columns.empty?
# We dont want attributes with whitespaces
columns = row.collect { |c| c.downcase.gsub(' ', '_') }
next
end
instances << create!(build_attributes(row, columns))
end
instances
end
private
def self.build_attributes(row, columns)
attrs = {}
columns.each_with_index do |column, index|
attrs[column] = row[index]
end
ap attrs
attrs
end
end
I am not aware of all fields and it may change in time. that's why I have create database and generic mehtods.
I have also another issue after having fixed the 'create!' issue.
I am using the encoding to make sure only UTF8 char are handled but I still see:
{
"zip" => "71964",
"cbsa" => "31680",
"res_ratio" => "0.086511098",
"bus_ratio" => "0.012048193",
"oth_ratio" => "0.000000000",
"tot_ratio" => "0.082435345"
}
when doing 'ap attrs' in the code. how to make sure that 'zip' -> 'zip'
Thanks
create! is a class method but you're trying to call it as an instance method. Your import! method shouldn't be an instance method either, it should be a class method since it produces instances of your class:
def self.import!(file_path)
#-^^^^
# everything else would be the same...
end
You'd also make build_attributes a class method since it is just a helper method for another class method:
def self.build_attributes
#...
end
And then you don't need that odd looking new call when using import!:
def self.create_all_databases
#cbsa2msa = DbForCsv.import!('./share/private/csv/cbsa_to_msa.csv')
#zip2cbsa = DbForCsv.import!('./share/private/csv/zip_to_cbsa.csv')
end

Rails 3 serialized model field form_for and field_for not generating correct name

I have this model:
class CompanyCrawler < ActiveRecord::Base
....
serialize :entry_pages, Array
def entry_page_objects
entry_pages.map { |url| EntryPage.new(url) }
end
def entry_page_objects_attributes=(attributes)
# ...
end
....
end
This form to render the model:
.....
%p
%p
= crawler_form.label 'Entry pages'
= crawler_form.text_area :entry_pages_text, size: '80x6'
%ul.entry-pages
= crawler_form.fields_for :entry_page_objects do |entry_page_field|
%li=entry_page_field.text_field :url, size: 80
%a{href: '#', class: 'add-button'} Add Entry Page
The problem I have is that the form renders the entry_page_object input names incorrectly(e.g. company_crawler[entry_page_objects_attributes][0][url] instead of company_crawler[entry_page_objects][0][url]). I am really not sure what to do, I have read the documentation and the example says that just by defining attr_attributes=(attributes) and persisted? I will be able to use fields_for for collections just if they were associations defined with accept_nested_fields.
I have seen different solutions like just giving String 'entry_page_objects[]' to fields_for but I want to be consistent with rails naming convention and I know I can use form_tag instead of form_for but I want to make fields_for work as expected.
Here is some information for all that have not understood properly how nested_attributes works, like me.
What I have reported as issue is actually how it is supposed to work. When we have, let say, this model:
class Foo < ActiveRecord::Base # it has name attribute
has_many :larodis
accepts_nested_attributes_for :larodi
end
class Larodi < ActiveRecord::Base # it has name attribute
belongs_to :foo
end
This definition gives me the possibility to create Foo with many Larodi's just by giving a hash of parameters. For example:
x = Foo.create(name: 'Josh', larodi_attributes: [ {name: 'Wayne'} ]
x.larodis.map(&:name) # ['Wayne']
Now comes the part where #field_for understands if we have nested attribute to work with. We check this by looking for name_attributes= method. If it is defined #fields_for generates form of the type <input ... name=object[name][INDEX][method]>... where index is just an integer.
Keep in mind that when implementing custom name_attibutes(attributes) you must check attributes type - it can be Array like the example, it can be Hash of this type:
{ 1 => { ... } , 2 => { ... } }
Just like a hash representing array, where the key is index and value is the value for this index.
The answear looks like this:
_form.html.haml
....
= crawler_form.fields_for :entry_pages do |entry_page_field|
%li
=entry_page_field.text_field :url, size: 80
...
company_crawler.rb
class CompanyCrawler < ActiveRecord::Base
....
serialize :entry_pages, Array
def entry_pages_attributes=(attributes)
self.entry_pages = attributes_collection(attributes).map do |attribute|
EntryPage.new(attribute[:url])
end
end
def entry_pages=(entry_pages)
entry_pages = entry_pages.map do |entry_page|
cast_entry_page_to_entry_page_object(entry_page)
end
write_attribute(:entry_pages, entry_pages)
end
...
private
def attributes_collection(attributes)
case attributes
when Array
attributes
when Hash
attributes.values
end
end
def cast_entry_page_to_entry_page_object(entry_page)
case entry_page
when String
EntryPage.new(entry_page)
when EntryPage
entry_page
end
end
end
For clarity I have removed entry_page_objects and use only entry_pages.

mongoid document to_json including all embedded documents without ':include' on each one

Given an arbitrary mongoid document how do i convert it to JSON and include any embedded structures without specifically including those structures in my to_json statement.
For example:
#!/usr/bin/env ruby
require 'mongoid'
require 'json'
require 'pp'
class Doc
include Mongoid::Document
include Mongoid::Timestamps
field :doc_specific_info , type: String
embeds_many :persons
end
class Person
include Mongoid::Document
field :role , type: String
field :full_name , type: String
embeds_many :addresses
embedded_in :Doc
end
class Address
include Mongoid::Document
field :full_address , type: String
end
doc = Doc.new
doc.doc_specific_info = "TestReport"
p = Person.new
p.role = 'buyer'
p.full_name = 'JOHN DOE'
doc.persons << p
a = Address.new
a.full_address = '1234 nowhere ville'
doc.persons.first.addresses << a
# THIS STATEMENT
pp JSON.parse(doc.to_json(:include => { :persons => { :include => :addresses } } ) )
# GIVES ME
# {"_id"=>"4ee0d30fab1b5c5743000001",
# "created_at"=>nil,
# "doc_specific_info"=>"TestReport",
# "updated_at"=>nil,
# "persons"=>
# [{"_id"=>"4ee0d30fab1b5c5743000002",
# "full_name"=>"JOHN DOE",
# "role"=>"buyer",
# "addresses"=>
# [{"_id"=>"4ee0d30fab1b5c5743000003",
# "full_address"=>"1234 nowhere ville"}]}]}
# THIS STATEMENT
pp JSON.parse(doc.to_json() )
# GIVES ME
# {"_id"=>"4ee0d2f8ab1b5c573f000001",
# "created_at"=>nil,
# "doc_specific_info"=>"TestReport",
# "updated_at"=>nil}
So what I want is a statement something like this:
# FOR A STATEMENT LIKE THIS
pp JSON.parse(doc.to_json( :everything } ) )
# TO GIVE ME THE COMPLETE DOCUMENT LIKE SO:
# {"_id"=>"4ee0d30fab1b5c5743000001",
# "created_at"=>nil,
# "doc_specific_info"=>"TestReport",
# "updated_at"=>nil,
# "persons"=>
# [{"_id"=>"4ee0d30fab1b5c5743000002",
# "full_name"=>"JOHN DOE",
# "role"=>"buyer",
# "addresses"=>
# [{"_id"=>"4ee0d30fab1b5c5743000003",
# "full_address"=>"1234 nowhere ville"}]}]}
Does such a statement exist? If not then is my only alternative recusing the structure of the document and producing the proper includes myself? If there is another way to visualize the whole document that would be better?
This was answered by rubish in the forum but he didn't post an answer so I am doing that.
The answer is to use "doc.as_document.as_json" which will give you the whole document.
pp doc.as_document.as_json
You can override the #to_json method in your document to add all include.
class Person
def to_json(*args)
super(args.merge({:include => { :persons => { :include => :addresses } } } )
end
end
Now you can have all by doing
person.to_json()
If you want return the complete with only :everything option you can do :
class Person
def to_json(*args)
if args[0] == :everything
super(args.merge({:include => { :persons => { :include => :addresses } } } )
else
super(args)
end
end
end

How to use ActiveRecord callbacks to assign field values before save?

I'm wondering how I can use callbacks to assign values to the database fields, which are processed out of a virtual attribute field.Example:
field :houseno, :type => String
field :street, :type => String
attr_accessor :address
My attempt at this seems to be unsuccessful. Here is what I have:
before_validation :assign_fields
def assign_fields
if #address
#houseno = #address.match(/^(\d+-?(\d+)?)\W*(.*)/)[1]
#street = #address.match(/^(\d+-?(\d+)?)\W*(.*)/)[3]
end
end
And I keep getting this error:
undefined method `houseno' for Building:0x0000010488f108
Have you tried:
write_attribute(:houseno) = #address.match(/^(\d+-?(\d+)?)\W*(.*)/)[1]
or
self.houseno = #address.match(/^(\d+-?(\d+)?)\W*(.*)/)[1]

Error happens when I read items in DataMapper

class List
include DataMapper::Resource
property :id, Serial
property :name, String
property :items, String
end
List.auto_migrate!
get '/:id' do
#list = List.all(:id => params[:id])
#items = #list.items
erb :show
end
I get undefined method `items' for #. Any ideas?
You fetch a collection of lists instead of a single list instance, that's why you get the error. I believe you want to do:
#list = List.get(params[:id])

Resources