what var type to dynamically access Model's attribute from another controller? (Rails 4.2) - ruby-on-rails

Goal: dynamically update another Model's properties (Tracker) from Controller (cards_controller.rb), when cards_controller is running the def update action.
Error I receive : NameError in CardsController#update, and it calls out the 2nd last line in the
def update_tracker(card_attribute) :
updated_array = #tracker.instance_variable_get("#{string_tracker_column}")[Time.zone.now, #card.(eval(card_attribute.to_s))]
Perceived problem: I have everything working except that I don't know the appropriate way to 'call' the attribute of Tracker correctly, when using dynamic attributes.
The attribute of the Tracker is an array (using PG as db works fine), I want to
figure out what property has been changed (works)
read the corresponding property array from Tracker's model, and make a local var from it. (works I think, )
push() a new array to the local var. This new array contains the datetime (of now) and, a string (with the value of the updated string of the Card) (works)
updated the Tracker with the correct attribute.
With the following code from the cards_controller.rb
it's the if #card.deck.tracked in the update method that makes the process start
cards_controller.rb
...
def update
#card = Card.find(params[:id])
if #card.deck.tracked
detect_changes
end
if #card.update_attributes(card_params)
if #card.deck.tracked
prop_changed?
end
flash[:success] = "Card info updated."
respond_to do |format|
format.html { render 'show' }
end
else
render 'edit'
end
end
...
private
def detect_changes
#changed = []
#changed << :front if #card.front != params[:card][:front]
#changed << :hint if #card.hint != params[:card][:hint]
#changed << :back if #card.back != params[:card][:back]
end
def prop_changed?
#changed.each do |check|
#changed.include? check
puts "Following property has been changed : #{check}"
update_tracker(check)
end
end
def update_tracker(card_attribute)
tracker_attribute = case card_attribute
when :front; :front_changed
when :back; :back_changed
when :hint; :hint_changed
end
string_tracker_column = tracker_attribute.to_s
#tracker ||= Tracker.find_by(card_id: #card.id)
updated_array = #tracker.instance_variable_get("#{string_tracker_column}")[Time.zone.now, #card.(eval(card_attribute.to_s))]
#tracker.update_attribute(tracker_attribute, updated_array)
end
Edit: For clarity here's the app/models/tracker.rb:
class Tracker < ActiveRecord::Base
belongs_to :card
end

Your use of instance_variable_get has been corrected, however this approach is destined to fail because ActiveRecord column values aren't stored as individual instance variables.
You can use
#tracker[string_column_changed]
#card[card_attribute]
To retrieve attribute values by name. If you want to get an association, use public_send. The latter is also useful if there is some accessor wrapping the column value (eg carrierwave)

From your error it seem your issue is this:
#tracker.instance_variable_get("#{string_tracker_column}")
evaluates to this after string interpolation:
#tracker.instance_variable_get("front_changed")
which is incorrect use of instance_variable_get. It needs an # prepended:
#tracker.instance_variable_get("#front_changed")
Seems like using instance_variable_get is unnecessary, though, if you set attr_reader :front_changed on the Tracker model.

Related

Rails saving arrays to separate rows in the DB

Could someone take a look at my code and let me know if there is a better way to do this, or even correct where I'm going wrong please? I am trying to create a new row for each venue and variant.
Example:
venue_ids => ["1","2"], variant_ids=>["10"]
So, I would want to add in a row which has a venue_id of 1, with variant_id of 10. And a venue_id of 2, with variant_id of 10
I got this working, and it's now passing in my two arrays. I think I am almost there I'm not sure the .each is the right way to do it, but I think that I'm on the right track haha. I have it submitting, however, where would I put my #back_bar.save? because this might cause issues as it won't redirect
Thanks in advance.
def create
#back_bar = BackBar.new
#venues = params[:venue_ids]
#productid = params[:product_id]
#variants = params[:variant_ids]
# For each venue we have in the array, grab the ID.
#venues.each do |v|
#back_bar.venue_id = v
# Then for each variant we associate the variant ID with that venue.
#variants.each do |pv|
#back_bar.product_variant_id = pv
# Add in our product_id
#back_bar.product_id = #productid
# Save the venue and variant to the DB.
if #back_bar.save
flash[:success] = "#{#back_bar.product.name} has been added to #{#back_bar.venue.name}'s back bar."
# Redirect to the back bar page
redirect_to back_bars_path
else
flash[:alert] = "A selected variant for #{#back_bar.product.name} is already in #{#back_bar.venue.name}'s back bar."
# Redirect to the product page
redirect_to discoveries_product_path(#back_bar.product_id)
end
end # Variants end
end # Venues end
end
private
def back_bar_params
params.require(:back_bar).permit(:venue_id,
:product_id,
:product_variant_id)
end
as i said in comments
this is untested code and just showing you how it's possible to do with ease.
class BackBar
def self.add_set(vanue_ids, variant_ids)
values = vanue_ids.map{|ven|
variant_ids.map{|var|
"(#{ven},#{var})"
}
}.flatten.join(",")
ActiveRecord::Base.connection.execute("INSERT INTO back_bars VALUES #{values}")
end
end
def create
# use in controller
BackBar.add_set(params[:venue_ids], params[:variant_ids])
# ...
end

How to pass parameter when duplicating object in rails controller?

How can I provide .dup method with custom param so every time it is executed param is always True even when object who is being duplicated has this param at false?
Attribute I want to make true is called :original.
Here is my Modifications_controller create action:
def create
#modification = Modification.new(change_params.merge(user: current_user))
respond_to do |format|
if #modification.save
#modification.entity.boxes.each do |b|
#modification.boxes << b.dup #here I need to pass custom param
end
format.js {}
else
format.js {}
end
end
end
#dup doesn't know anything specific about your model logic. If you want to set some attributes to true, simply clone the object and then change the values.
box = b.dup
box.value = true
#modification.boxes << box
You can also consider to extract the feature in a custom method in the model, so that it's easier to write a test for it.
def duplicate
self.dup.tap do |i|
i.value = true
end
end
#modification.boxes << b.duplicate

Call 'structure' to save in postgres using rails

I have a 'publication' data structure, and my table name in postgres is 'publications'. I get a tweet from streaming and parse it according in class TweetFetcher. How do I call publication controller to save it to the database? PublicationController has the standard scrum abilities (new, create, show, edit...), where create is:
def create
#publication = Publications.new(params[:publication])
if #publication.save
redirect_to :action => 'list'
else
#subjects = Subject.find(:all)
render :action => 'new'
end
end
and parte of my twitter code is
class TweetFetcher
def saveTweet(parsedTweet)
pT = JSON.parse(parsedTweet)
#save here. like this?
#PublicationController.create(parsedTweet)
end
end
You shouldn't be using your controller in this situation, instead you can just build your record like this:
class TweetFetcher
def saveTweet(parsedTweet)
pT = JSON.parse(parsedTweet)
publication = Publications.new
publication.sample_field = pT.sample_field
... # set additional attributes
publication.save
end
end
Additionally, here is some advice/information that is not directly related to your question, but are things you should know:
The class name of your models should be singular, so instead of Publications, it should be Publication.
Method and variable names should be in snake case rather than camel case (i.e. save_tweet instead of saveTweet or parsed_tweet instead of parsedTweet).
The new hash syntax is prefered, unless of course you are using a Ruby version below 1.9. This looks like { key: value } instead of { key => value }.
Indentation for Ruby code is typically 2 spaces.
That being said, I would change your code like this:
PublicationsController#create
def create
#publication = Publication.new(params[:publication])
if #publication.save
redirect_to action: 'list'
else
#subjects = Subject.find(:all)
render action: 'new'
end
end
TweeFetcher
class TweetFetcher
def save_tweet(tweet)
parsed_tweet = JSON.parse(tweet)
publication = Publication.new
publication.sample_field = parsed_tweet.sample_field
... # set additional attributes
publication.save
end
end

RAILS V2.3 - (redmine) new unique field value

I am writing a little plugin for my company redmine to assign unique documents [progressive] codes
the code I wrote so far works, but I don't think it is multi threads proof as there is a chance two users get the same document code.
I would like to find a way to lock the table while getting the last number and creating the new record with the incremented document number (D9999)
This is the active record:
class Documenti < ActiveRecord::Base
unloadable
def self.nextest
record=self.last
if (record.nil?) then ultimo=sprintf("D0000")
elsif (record.codice.nil?) then ultimo=sprintf("D0000")
else ultimo=record.codice
end
if (/^D[0-9]{4}/ =~ ultimo) == 0 then
c=ultimo.split("D")
p=c[1].to_i + 1
t=sprintf("D%04i",p)
end
return t
end
end
Controller then is like this:
def new
#documenti = Documenti.new
#documenti.codice=Documenti.nextest
respond_to do |format|
format.html # new.html.erb
end
end
so far the nextest value is not saved and another user can take the same value.

Rails - 'can't dump hash with default proc' during custom validation

I have 2 models. User and Want. A User has_many: Wants.
The Want model has a single property besides user_id, that's name.
I have written a custom validation in the Want model so that a user cannot submit to create 2 wants with the same name:
validate :existing_want
private
def existing_want
return unless errors.blank?
errors.add(:existing_want, "you already want that") if user.already_wants? name
end
The already_wants? method is in the User model:
def already_wants? want_name
does_want_already = false
self.wants.each { |w| does_want_already = true if w.name == want_name }
does_want_already
end
The validation specs pass in my model tests, but my feature tests fail when i try and submit a duplicate to the create action in the WantsController:
def create
#want = current_user.wants.build(params[:want])
if #want.save
flash[:success] = "success!"
redirect_to user_account_path current_user.username
else
flash[:validation] = #want.errors
redirect_to user_account_path current_user.username
end
end
The error I get: can't dump hash with default proc
No stack trace that leads to my code.
I have narrowed the issue down to this line:
self.wants.each { |w| does_want_already = true if w.name == want_name }
if I just return true regardless the error shows in my view as I would like.
I don't understand? What's wrong? and why is it so cryptic?
Thanks.
Without a stack trace (does it lead anywhere, or does it just not appear?) it is difficult to know what exactly is happening, but here's how you can reproduce this error in a clean environment:
# initialize a new hash using a block, so it has a default proc
h = Hash.new {|h,k| h[k] = k }
# attempt to serialize it:
Marshal.dump(h)
#=> TypeError: can't dump hash with default proc
Ruby can't serialize procs, so it wouldn't be able to properly reconstitute that serialized hash, hence the error.
If you're reasonably sure that line is the source of your trouble, try refactoring it to see if that solves the problem.
def already_wants? want_name
wants.any? {|want| want_name == want.name }
end
or
def already_wants? want_name
wants.where(name: want_name).count > 0
end

Resources