I have a method in my model Post like this:
def self.post_template
posts = Post.all
result = []
posts.each do |post|
single_post = {}
single_post['comment_title'] = post.comment.title
single_post['comment_content'] = post.comment.content
result << single_post
end
# return the result
result
end
In one of my rake tasks, I call the function:
namespace :post do
task :comments => :environment do
comments = Post.post_template
puts comments
end
end
In the console, the return value isn't an Array; instead, it prints all the hashes separated by a newline:
{ 'comment_title' => 'stuff', 'comment_content' => 'content' }
{ 'comment_title' => 'stuff', 'comment_content' => 'content' }
{ 'comment_title' => 'stuff', 'comment_content' => 'content' }
However, when I run this in my rails console, I get the expected behavior:
> rails c
> comments = Post.post_template
-- [{ 'comment_title' => 'stuff', 'comment_content' => 'content' },
{ 'comment_title' => 'stuff', 'comment_content' => 'content' }]
Needless to say, I'm pretty confused and would love any sort of guidance! Thank you.
EDIT:
Seems rake tasks simply print out arrays like this, but when I set the result of my array into another hash, it does not seem to maintain the integrity of the array:
namespace :post do
task :comments => :environment do
comments = Post.post_template
data = {}
data['messages'] = comments
end
end
I'm using Mandrill (plugin for Mailchimp) to create these messages and it throws an error saying that what I'm passing in isn't an Array.
I think that's just how rake prints arrays. Try this:
task :array do
puts ["First", "Second"]
end
Now:
> rake array
First
Second
Related
I'm trying to create a workout routine that gets created when a workout gets created by passing this in via ajax:
Parameters: {"utf8"=>"✓", "workout"=>{"name"=>"cool workout", "desc"=>"super cool"}, "exerciseorder"=>["4", "2"], "repssets"=>{"0"=>["40", "4"], "1"=>["60", "6"]}}
Here is what my Create action looks like in my Workout Controller:
exercise_order = params[:exerciseorder]
repssets = params[:repssets]
#workout = Workout.new(workout_params)
if #workout.save
WorkoutRoutine.create(
[
exercise_order.each_with_index.map { |x,i|
{
:exercise_id => x,
:position => i,
:workout_id => #workout.id
}
},
repssets.map { |x| x.last }.each { |y|
{
:reps => y.first,
:sets => y.last
}
}
]
)
render :nothing => true
else
render json: #workout.errors.full_messages, status: :unprocessable_entity
end
If I use an opening and closing '[]' within the WorkoutRoutine.create, it tells me:
ArgumentError: When assigning attributes, you must pass a hash as an argument.
And when I change them to '{}' it tells me:
syntax error, unexpected ',', expecting =>
I've tried a myriad of different combinations and work-arounds but can't seem to figure out why it won't correctly parse the data and save it to the database, any help is very appreciated.
EDIT:
When I remove the initial {} and [] from the WorkoutRoutine.create:
WorkoutRoutine.create(
exercise_order.each_with_index.map { |x,i|
{
:exercise_id => x,
:position => i,
:workout_id => 20
}
},
repssets.map { |x| x.last }.each { |y|
{
:reps => y.first,
:sets => y.last
}
}
)
I get this error message:
ArgumentError: wrong number of arguments (2 for 0..1)
Edit2:
This is the jQuery code that sents to the data field via ajax:
var getId = $(".exercise-list").sortable('toArray');
ary = []
$(getId).each(function () {
id = $(this[0]).selector;
var reps = $("#reps" + id).val();
var sets = $("#sets" + id).val();
ary.push([reps, sets])
});
var orderRepsSets = { exerciseorder: getId, repssets: ary }
var formData = $('#new_workout').serialize();
var data = formData + '&' + $.param(orderRepsSets);
$.ajax({
url: $("#new_workout").attr('action'),
method: 'POST',
data: data,
success: (function() {
....
});
Did I get it correctly that you want to create multiple WorkloadRoutine objects, one for each exercise with the corresponding repetitions, the position, etc. If yes, then in order to do that you will have to pass an array of hashes (one hash for each object) to the WorkoutRoutine.create() function. As engineersmnky correctly stated in his comment, the data structure you are currently passing is more like [[{...},{...},{...}],[{...},{...},{...}]], but instead it should be just [{...},{...},...]. Do achieve that, something like this should do the trick:
WorkoutRoutine.create(
exercise_order.map.with_index { |x,i|
{
:exercise_id => x,
:position => i,
:workout_id => #workout.id,
:reps => repssets[i.to_s].first,
:sets => repssets[i.to_s].last
}
}
)
If you could change repssets to an array like exercise_order you could even remove the string cast for getting the reps and sets, which would simplify the whole think even more.
If it comes for errors they are quite self explanatory. But let's start from beginning..
I assume that WorkoutRoutine is an ActiveRecord::Base model. The WorkoutRoutine.create method gets 0 or 1 argument which should be a Hash or a block.
In the first iteration you were passing an Array instead of Hash, so it looked like:
WorkoutRoutine.create([a, b]) # => ArgumentError: When assigning attributes, you must pass a hash as an argument.
On the second iteration you stripped away the square brackets, what gave you 2 arguments instead of one Hash:
WorkoutRoutine.create(a, b) # => ArgumentError: wrong number of arguments (2 for 0..1)
If you read errors carefully you will start getting the idea what's happening.
About the workout routine itself
From what you specified I would assume that you want something like:
Workout has many Routines
Routine belongs to Workout and Exercise
Routine is composed of fields like
position/order,
number of repetitions,
number of sets
If my assumption is correct, then you want to use nested_attributes and then have parameters and controller like
# given parameters as workout_params
{
workout: {
name: "workout name",
desc: "workout description",
routines_attributes: [
{ position: 1, exercise_id: 4, repetitions_number: 40, sets_number: 4 },
{ position: 2, exercise_id: 2, repetitions_number: 60, sets_number: 6 }
]
}
}
# Controller
def create
#workout = Workout.new workout_params
if #workout.save
redirect_to # ...
else
render :new
end
end
private
def workout_params
params.require(:workout).permit(:name, :desc, routines_attributes: [:position, :exercise_id, :repetitions_number, :sets_number])
end
It should be strait forward how to then create a view with fields_for and update models with proper associations
I am new to WSDL.
Code (I have added in the view directly - for test): (Page: http://localhost:3000/ccapis )
require 'savon'
client = Savon::Client.new(wsdl: "http://localhost:3000/ccapis/wsdl")
result = client.call(:fetch_prizes, message: { :gl_id => "123456789" })
result.to_hash
And in the controller:
soap_action "fetch_prizes",
:args => { :gl_id => :string },
:return => [:array]
def fetch_prizes
glnumber = params[:gl_id ]
prize = Prize.where(:gl_id => glnumber)
prize_to_show = []
a_hash = {}
prize.each do |p|
a_hash = { :prize => p.prize.to_s, :score => p.score.to_s, :date => p.round_date.to_s }
prize_to_show.push a_hash
a_hash = nil
end
render :soap => prize_to_show
end
When I try and run this in the Console all are good and I can see the result.to_hash but when I go to http://0.0.0.0:3000/ccapis I get the error that I mentioned above.
Explanation of what I am trying to achieve:
I need to supply a WSDL for a client which fetches all the prizes based on a score.
If My approach is wrong please direct me to a document so I can have a read and get a better understanding. Thanks again.
I'm trying to create a json array (string actually) based on my db structure. I have the following relationship:
Country > State > City
The way I'm doing it now is very innefficient (N+1):
data = "[" + Country.all.map{ |country|
{
name: country.name,
states: country.states_data
}.to_json
}.join(",") + "]"
Then on the Country model:
def states_data
ret_states = []
states.all.each do |state|
ret_states.push name: state.name, cities: state.cities_data
end
ret_states
end
Then on the State model:
def cities_data
ret_cities = []
cities.all.each do |city|
ret_cities.push name: city.name, population: city.population
end
ret_cities
end
How can I do this more efficiently?
Eager load the states and cities. Just be careful because this could take up a lot of memory for large datasets. See documentation here http://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations. Whenever possible I like using joins in addition to includes to fetch all data at once.
#to_json will also serialize Arrays for you, so you don't need to manually add bits of JSON.
Your code from above could be altered like so:
data = Country.joins(:states => :cities).includes(:states => :cities).all.map{ |country|
{
name: country.name,
states: country.states_data
}
}.to_json
But you could also remove the need for the _data methods.
data = Country.joins(:states => :cities).includes(:states => :cities).to_json(
:only => :name,
:include => {
:states => {
:only => :name,
:include => {
:cities => {
:only => [:name, :population]
}
}
}
}
)
That is pretty ugly, so you may want to look into overriding #as_json for each of your models. There is a lot of information about that available on the web.
u can provide the model to be included when converting to json.
country.to_json(:include => {:states => {:include => :cities}})
check http://apidock.com/rails/ActiveRecord/Serialization/to_json for
I read a few other posts on this subject and am still confused. In my seeds.rb I call delete and create on the model without any issues... when I get to the custom methods I created I get an undefined method error. The create and delete_all work fine when I comment out the name_gen and ssn_gen rows.
Also, this is Rails 3.1.1 on Ruby 1.8.7
Update: should have also mentioned I get the same issue if I change the create to new and move the name_gen sections to something like #sample_data_set.officialFirstName = SampleDataSet.name_gen
Error: undefined method `name_gen' for #
Command for rake: bundle exec rake db:seed RAILS_ENV=development --trace
seeds.rb
SampleDataSet.delete_all
#sample_data_set = SampleDataSet.new (
:campusNum => "96",
:dateOfBirth => "1981-10-09",
:gender => "M",
:officialMiddleInitial => "L",
:addressLine1 => "PO BOX 9",
:addressLine2 => "",
:city => "WOODLAND",
:state => "GA",
:zipCode => "31836",
:homeAreaCode => "706",
:homePhoneNumber => "6742435",
:homePhoneCountryCode => "US",
:workAreaCode => "706",
:workPhoneNumber => "6742435",
:workPhoneCountryCode => "US",
:usCitizen => true,
:financialAid => true,
:previousDegree => "ADN",
:region => "MAIN",
:program => "AAPSY",
:version => "012",
:team => "TEAM 3236A",
:enrollmentUserId => "SSGROTH",
:revCampusOid => "1627",
:executingUserId => "QROBINSO",
:totalDeclaredExtCredits => "1",
#generating some default values for the gen fields... except IRN
:officialFirstName => SampleDataSet.name_gen,
:officialLastName => SampleDataSet.name_gen,
:enrollAgreeSignDate => Date.today.strftime('%Y-%m-%d'),
:scheduledStartDate => Date.tomorrow.strftime('%Y-%m-%d'),
:ssn => SampleDataSet.ssn_gen.to_s
)
#sample_data_set.emailAddresses = officialFirstName + "." + officialLastName + "#aaaa.phoenix.edu"
,
SampleDataSet model
class SampleDataSet < ActiveRecord::Base
#Random info generation
def name_gen(*prepend)
#Random character generation piece I found on Stackoverflow with 102 upvotes
character_map = [('a'..'z'),('A'..'Z')].map{|i| i.to_a}.flatten
name = (0..8).map{ character_map[rand(character_map.length)] }.join
if prepend.nil?
return name
else
return prepend.to_s + "_" + name
end
end
def ssn_gen
#broke this out as its own method in case someone wants some logic later on
ssn = Random.rand(1000000000) + 99999999
return ssn
end
end
In order to call some method directly on class like that:
SampleDataSet.name_gen
instead of calling it on an instance of that class (as regular methods are called) like that:
a = SampleDataSet.new
a.name_gen
you should define that method as a class method.
You can do it using self.name_gen instead of name_gen in method definition like that:
class SampleDataSet < ActiveRecord::Base
# Random info generation
def self.name_gen(*prepend)
# Random character generation piece I found on Stackoverflow with 102 upvotes
character_map = [('a'..'z'), ('A'..'Z')].map { |i| i.to_a }.flatten
name = (0..8).map { character_map[rand(character_map.length)] }.join
if prepend.nil?
return name
else
return prepend + "_" + name
end
end
def self.ssn_gen
# broke this out as its own method in case someone wants some logic later on
ssn = Random.rand(1000000000) + 99999999
return ssn
end
end
How am I able to create a hash within a hash, with the nested hash having a key to indentify it. Also the elements that I create in the nested hash, how can I have keys for them as well
for example
test = Hash.new()
#create second hash with a name?? test = Hash.new("test1")??
test("test1")[1] = 1???
test("test1")[2] = 2???
#create second hash with a name/key test = Hash.new("test2")???
test("test2")[1] = 1??
test("test2")[2] = 2??
thank you
Joel's is what I would do, but could also do this:
test = Hash.new()
test['test1'] = Hash.new()
test['test1']['key'] = 'val'
my_hash = { :nested_hash => { :first_key => 'Hello' } }
puts my_hash[:nested_hash][:first_key]
$ Hello
or
my_hash = {}
my_hash.merge!(:nested_hash => {:first_key => 'Hello' })
puts my_hash[:nested_hash][:first_key]
$ Hello
h1 = {'h2.1' => {'foo' => 'this', 'cool' => 'guy'}, 'h2.2' => {'bar' => '2000'} }
h1['h2.1'] # => {'foo' => 'this', 'cool' => 'guy'}
h1['h2.2'] # => {'bar' => '2000'}
h1['h2.1']['foo'] # => 'this'
h1['h2.1']['cool'] # => 'guy'
h1['h2.2']['bar'] # => '2000'