How to get push value key in Firebase Ruby REST wrapper - ruby-on-rails

I am working on a project to do CRUD Operations to firebase. I made use of this to help facilitate and link my ruby project to firebase.
Functions:
def delete_firebase(event_params,rootpath="Events/")
query = init_firebase.delete(rootpath,event_params)
end
def new_firebase(event_params,rootpath="Events")
query = init_firebase.push(rootpath,event_params)
end
def init_firebase # Inits firebase project with URL and secret
firebaseURL = "myfirebaseprojecturl"
firebaseSecret = "myfirebasesecret"
firebase = Firebase::Client.new(firebaseURL, firebaseSecret)
end
Event params consist of my event parameters as shown below
def event_params
params.require(:event).permit(:eventID, :eventName, :attachment, :eventNoOfPpl, :eventAdminEmail, {eventpics: []})
end
I encountered an issue. When I push with push() into firebase, there is a random key like -LSFOklvcdmfPOWrxgBo. In such case, the structure of the document would look like this:
But I cannot delete anything from -LSFOklvcdmfPOWrxgBo as I do not have the value. I used delete() from Oscar's firebase-ruby gem. I would appreciate any help with this issue.

I re-read the gem docs, and got some help from my friends and came up with two solutions
The body's response has response.body # => { 'name' => "-INOQPH-aV_psbk3ZXEX" } and thus, you're able to find out the name if you'd like
Change the index, and don't use .push, instead I made use of .set and did a random number for every event
Final solution
def load_firebase(root_path = "Events")
firebase_json = init_firebase.get(root_path)
if valid_json?(firebase_json.raw_body)
#json_object = JSON.parse(firebase_json.raw_body)
end
end
def update_firebase(event_params, root_path = "Events/")
init_firebase.update("#{root_path}#{event_params["eventID"]}", event_params)
end
def delete_firebase(event_params, root_path = "Events/")
init_firebase.delete("#{root_path}#{event_params["eventID"]}")
end
def save_firebase(event_params, root_path = "Events/")
init_firebase.set("#{root_path}#{event_params["eventID"]}", event_params)
end

Related

Why is my object getting serialized into a string not text in my Rails ActiveRecord DB?

I am currently trying to store a user object accessed from the Soundcloud API into my local rails ActiveRecord database. The object acts like a hash.
This is what the object/hash looks like:
#<SoundCloud::HashResponseWrapper avatar_url="https://i1.sndcdn.com/avatars-000296065573-ewlbh2-large.jpg" city="Brooklyn" country="United States" description="Raising The Bar Since 2007" discogs_name=nil first_name="Fool's" followers_count=7993682 followings_count=84 full_name="Fool's Gold" id=5636679 kind="user" last_modified="2018/03/09 19:40:55 +0000" last_name="Gold" myspace_name=nil online=false permalink="foolsgoldrecs" permalink_url="http://soundcloud.com/foolsgoldrecs" plan="Pro Plus" playlist_count=250 public_favorites_count=808 reposts_count=449 subscriptions=#<Hashie::Array [#<SoundCloud::HashResponseWrapper product=#<SoundCloud::HashResponseWrapper id="creator-pro-unlimited" name="Pro Unlimited">>]> track_count=1037 uri="https://api.soundcloud.com/users/5636679" username="Fool's Gold Records" website="http://smarturl.it/FoolsGoldSpotify" website_title="Spotify">
My ActiveRecord Schema.rb
ActiveRecord::Schema.define(version: 20180323143520) do
create_table "soundcloud_users", force: :cascade do |t|
t.string "user_name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.text "user_hash"
end
end
What I have tried:
Researching how to serialize objects in Ruby I found that I was supposed to make the field-to-be-serialized of type 'text'. I have made two different attempts at serializing then de-serializing the data, all of which have failed. Here is what I did:
1) Serialize as a Hash.
Code I tried:
class SoundcloudUser < ApplicationRecord
serialize :user_hash, Hash
end
I then get this Error:
TypeError in SoundcloudQueriesController#create
allocator undefined for Proc
2) Serialize using YAML:
Code I tried:
class SoundcloudUser < ApplicationRecord
YAML::dump(:user_hash)
end
This time I don't get an error. When I try to access the hash I wrote this code in my index def just to display it quickly:
def index
#user = SoundcloudUser.find(2)
#user_hash = YAML::load(#user.user_hash)
end
However, I for some reason cannot access the parameters the way I expect i.e. #user_hash.followers_count.
So I decided to go into rails console and see what was going on:
> #user = SoundcloudUser.find(6)
> #user_hash = #user.user_hash
> #user_hash.class
=> String
So for some reason, even though the field in active record is text, it is getting stored as a string? Or maybe the read back is converting it to a string? I really don't know what's going on, but am a bit lost. It's my first time serializing data, and the Soundcloud API has always returned weird objects that never work the way I expect. SUPER appreciate any help or advice!
EDIT #2:
Trying with JSON as per suggestion
When I changed my code as you prescribed, I still get the same issue:
SoundCloudQueriesController.rb
def index
end
def create
#user_url = params[:soundcloud_url]
#soundcloud_user = SoundcloudUser.new
#query = Query.new
#user = #query.query_user(#user_url)
#soundcloud_user.user_name = #user.username
#soundcloud_user.user_hash = JSON.parse(#user.to_json).symbolize_keys
#soundcloud_user.save
end
SoundcloudUser.rb (model)
class SoundcloudUser < ApplicationRecord
end
Output (querying the database in rails console):
Typing in the following commands into rails console:
#user = SoundcloudUser.find(7)
#user_hash = #user.user_hash
This gives the following output of the hash (which still seems to be weirdly formatted with the "\" that weren't there before):
=>
"{:id=>8, :kind=>\"user\", :permalink=>\"alex\", :username=>\"🔥𝔞𝔩𝔢𝔵 🔥\", :last_modified=>\"2018/03/15 18:15:56 +0000\", :uri=>\"https://api.soundcloud.com/users/8\", :permalink_url=>\"http://soundcloud.com/alex\", :avatar_url=>\"https://i1.sndcdn.com/avatars-000208970259-vngd3i-large.jpg\", :country=>\"Germany\", :first_name=>\"Alexander\", :last_name=>\"Ljung\", :full_name=>\"Alexander Ljung\", :description=>\"Hi, my name is Alex. I like Sound. Founder & Chairman, SoundCloud.\", :city=>\"Berlin/NYC\", :discogs_name=>nil, :myspace_name=>nil, :website=>nil, :website_title=>nil, :track_count=>268, :playlist_count=>34, :online=>false, :plan=>\"Pro Plus\", :public_favorites_count=>1601, :followers_count=>185322, :followings_count=>1697, :subscriptions=>[{\"product\"=>{\"id\"=>\"creator-pro-unlimited\", \"name\"=>\"Pro Unlimited\"}}], :reposts_count=>132}"
When I try to access the hash as you prescribed:
> #user_hash[:followers_count]
I get the following message:
TypeError: no implicit conversion of Symbol into Integer
from (irb):6:in `[]'
from (irb):6
Weirdly enough, it gives the same error even for fields that are not integer values like :followers_count (:username for example).
I think you are right, this is not a hash. So now I am super lost at how to store this in my database haha. I tried just storing it with no serialization, but I get the same issue of it being seemingly converted to a string.
I see in your source code:
class Query
def initialize
#client = Soundcloud.new(:client_id => API_KEY)
end
def query_user(user_url)
#user_url = user_url
#user = #client.get('/resolve', :url => #user_url)
end
end
At this point #user is a still a SoundCloud::HashResponseWrapper object.
If you wanna store the data as string text you'll need to first do:
#user.to_json
To save it with symbolized keys in the db:
data = JSON.parse(#user.to_json).symbolize_keys
sc_user = SoundcloudUser.new(user_hash: data)
But you still can't call .key on a hash. If you want to call the value of the key you'll need to do:
sc_user.user_hash[:username] #for example
There are ways to extend your model but that's out of scope for this question.
UPDATE: Here's your updated controller:
class SoundcloudQueriesController < ApplicationController
def new
end
def index
#user = SoundcloudUser.find(3)
#user_hash = #user.user_hash # but do you even need this?
end
def create
sc_user_data = Query.new.get_user params[:username]
#soundcloud_user = SoundcloudUser.create({
user_name: sc_user_data[:username],
user_hash: JSON.parse(sc_user_data.to_json).symbolize_keys
})
end
def show
end
end
You'll also need to modify your Query class to be able to build a user from the souncloud username. It will look like this:
class Query
BASE_RESOLVE_URL="http://soundcloud.com/"
def initialize
#client = Soundcloud.new :client_id => ENV['SC_CLIENT_ID']
end
def query_user(user_url)
#user_url = user_url
#user = #client.get('/resolve', :url => #user_url)
end
def get_user(username)
#client.get('/resolve', url: BASE_RESOLVE_URL+username)
end
end
I'm not exactly sure what you want to use the query_user method for, or if you can just remove it and use get_user instead. In any case I forked your repo, got the controller create test passing. Will send PR.

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

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

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.

Spree error when using decorator with the original code

Need a little help over here :-)
I'm trying to extend the Order class using a decorator, but I get an error back, even when I use the exactly same code from source. For example:
order_decorator.rb (the method is exactly like the source, I'm just using a decorator)
Spree::Order.class_eval do
def update_from_params(params, permitted_params, request_env = {})
success = false
#updating_params = params
run_callbacks :updating_from_params do
attributes = #updating_params[:order] ? #updating_params[:order].permit(permitted_params).delete_if { |k,v| v.nil? } : {}
# Set existing card after setting permitted parameters because
# rails would slice parameters containg ruby objects, apparently
existing_card_id = #updating_params[:order] ? #updating_params[:order][:existing_card] : nil
if existing_card_id.present?
credit_card = CreditCard.find existing_card_id
if credit_card.user_id != self.user_id || credit_card.user_id.blank?
raise Core::GatewayError.new Spree.t(:invalid_credit_card)
end
credit_card.verification_value = params[:cvc_confirm] if params[:cvc_confirm].present?
attributes[:payments_attributes].first[:source] = credit_card
attributes[:payments_attributes].first[:payment_method_id] = credit_card.payment_method_id
attributes[:payments_attributes].first.delete :source_attributes
end
if attributes[:payments_attributes]
attributes[:payments_attributes].first[:request_env] = request_env
end
success = self.update_attributes(attributes)
set_shipments_cost if self.shipments.any?
end
#updating_params = nil
success
end
end
When I run this code, spree never finds #updating_params[:order][:existing_card], even when I select an existing card. Because of that, I can never complete the transaction using a pre-existent card and bogus gateway(gives me empty blanks errors instead).
I tried to bind the method in order_decorator.rb using pry and noticed that the [:existing_card] is actuality at #updating_params' level and not at #updating_params[:order]'s level.
When I delete the decorator, the original code just works fine.
Could somebody explain to me what is wrong with my code?
Thanks,
The method you want to redefine is not really the method of the Order class. It is the method that are mixed by Checkout module within the Order class.
You can see it here: https://github.com/spree/spree/blob/master/core/app/models/spree/order/checkout.rb
Try to do what you want this way:
Create file app/models/spree/order/checkout.rb with code
Spree::Order::Checkout.class_eval do
def self.included(klass)
super
klass.class_eval do
def update_from_params(params, permitted_params, request_env = {})
...
...
...
end
end
end
end

metaprogramming for params

How can I update these very similar text fields in a less verbose way? The text fields below are named as given - I haven't edited them for this question.
def update
company = Company.find(current_user.client_id)
company.text11 = params[:content][:text11][:value]
company.text12 = params[:content][:text12][:value]
company.text13 = params[:content][:text13][:value]
# etc
company.save!
render text: ""
end
I've tried using send and to_sym but no luck so far...
[:text11, :text12, :text13].each do |s|
company.send("#{s}=".to_sym, params[:content][s][:value])
end
If they are all incremental numbers, then:
11.upto(13).map{|n| "text#{n}".to_sym}.each do |s|
company.send("#{s}=".to_sym, params[:content][s][:value])
end
I'd consider first cleaning up the params, then move onto dynamically assigning attributes. A wrapper class around your params would allow you to more easily unit test this code. Maybe this helps get you started.
require 'ostruct'
class CompanyParamsWrapper
attr_accessor :text11, :text12, :text13
def initialize(params)
#content = params[:content]
content_struct = OpenStruct.new(#content)
self.text11 = content_struct.text11[:value]
self.text12 = content_struct.text12[:value]
self.text13 = content_struct.text13[:value]
end
end
# Company model
wrapper = CompanyParamsWrapper.new(params)
company.text11 = wrapper.text11
# now easier to use Object#send or other dynamic looping

Resources