Deserialize JSON without gems or PostgreSQL in Rails 5 - ruby-on-rails

I am saving a small JSON fragment in the database as follows but am stuck retrieving the data in a usable way.
In this app, Rails is getting the JSON via a fetch AJAX call, and successfully saving the following in the decor field:
{"drapes": "red", "table": "wood", "candles":"true"}
In the Rails console, this comes out as expected:
> p = Party.last
=> ...
> p.decor
=> "{:drapes=>\"red\", :table=>\"wood\", :candles=>\"true\"}"
decor is a (datatype) text field in SQLite at the moment. I've used PostgreSQL before for JSON but don't want to go there right now on this project and given the official doc reference (below), should be able to manage without any additional gems.
Here's the model (party.rb) as recommended by https://api.rubyonrails.org/classes/ActiveRecord/AttributeMethods/Serialization/ClassMethods.html:
class Party < ApplicationRecord
serialize :decor, JSON
end
And here's the relevant action in the controller (party_controller.rb):
def create
#party = Party.new(party_params)
#party.save
end
So all good until I actually want to to extract the decor and (for example) show it in a view template. With the following (in parties/index.html.erb)...
<p>
<%= p.decor.drapes %>
</p>
... I get:
NoMethodError in Parties#index
undefined method `drapes'
p.decor is obviously coming out of the model as the wrong data type but despite various experiments in the console, I can't see how to extract the JSON values using their keys.

Related

Passing rails objects or variables to liquid

I wrote an app for shopify which works as designed. Now I wanted to add the app proxy to make the content embedded in the shop. The proxy is working fine but shopify expects a response (in my case a view) as pure liquid. The original view in ruby has some logic using several variables and objects.
I installed the liquid gem. The tutorials I walked through seem to be a little bit outdated.
#template = Liquid::Template.parse("hi {{name}}") # Parses and compiles the template
#template.render( 'name' => 'tobi' ) # Renders the output
=> "hi tobi"
This is working, but it only works with strings. I watched the railscast which I thought will be an easy way to allow liquid to access the objects. But again its outdated and the "liquid_methods" isn't working any more.
So i wrote my own method in my model:
def to_liquid
{ "title" => self.title,
"description" => self.description
}
If i try to render it with my method in my console like this:
#template = Liquid::Template.parse("Welcome to {{object.title}}")
#template.render(object.title.to_liquid)
I get the following error:
Liquid::ArgumentError (Liquid error: Expected Hash or Liquid::Context as parameter)
Now Im stuck...
What is the most simple way to access RoR Objects with liquid?
After trying almost every possible way to pass the variable, this did the trick:
#object = Object.find(params[:id])
#hash_object = #object.to_liquid
#template = Liquid::Template.parse("Welcome to {{object_title}}")
#template.render('object_title' => #hash_object["title"])

How to get Angular 5 temple name attribute to match Rails 5 attachment attribute

I am working on an application the uses a Angular 5 frontend and the Rails 5 backend. Both are APIs that communicate through JSON. Further, for uploading I am using the Shrine gem with Uppy. To get uploading up and running I followed these two tutorials:
https://github.com/shrinerb/shrine-tus-demo,
https://github.com/shrinerb/shrine/wiki/Adding-Resumable-Uploads
The problem that I am running into is that to send data to the rails, the uploaded name attribute from the temple and the rails attachment must match for the data to be passed off to the rails application. So, from the tutorials, this:
# models/movie.rb
class Movie < Sequel::Model
include VideoUploader::Attachment.new(:video)
end
must match the name attribute from this:
<div class="form-group">
<input type="hidden" name="movie[video]" value="<%= #movie.cached_video_data %>" class="upload-hidden">
<input type="file" name="movie[video]" class="upload-file">
</div>
As I am not using a Rails frontend, the data is not so easily passed. Is there some way to reconcile the two. I've tried passing the following to the Rails API as the name attributes value:
name="movie[video]"
name="video"
I have even tried sending JSON values like:
name="{
"id":"http://endpoint.com/files/226eed1388bbd1b7f029897ad2b86f16",
"storage":"cache",
"metadata":{
"filename":"SampleVideo_720x480_1mb.mp4",
"size":1057149,
"mime_type":"video/mp4"
}
}"
As this matches the same JSON format from when I can movie.video in the rails console.
Is there any way to make this work?
EDIT:
Okay, I have asked the wrong question. Sorry, this is my first project that's not following a tutorial. I have spent a lot of time trying to make this work:
name="{
"movie": {
"video": {
"id":"http://endpoint.com/files/226eed1388bbd1b7f029897ad2b86f16",
"storage":"cache",
"metadata":{
"filename":"SampleVideo_720x480_1mb.mp4",
"size":1057149,
"mime_type":"video/mp4"
}
}
}
}"
before I realized that data is being sent via tus-js-client and tus-server. So, the right question should be how to send the correct name attribute to the tus-js-client so it can be sent to the tus-server and finally passed to the Rails application. For more information on the backend, here is the create action and movie params:
def create
movie = Movie.new(movie_params)
if movie.save
render json: movie, status: 201
else
render json: { errors: movie.errors }, status: 422
end
end
private
def movie_params
# params.permit(:title, :year, :plot, :video_data)
params.require(:movie).permit(video: [metadata: [:title, :year, :plot]])
# params.require(:movie).permit!
end
Also, for how I am sending this information, I am using Uppy's in built submission. This forgoes Angular usual submission methods and therefore submitted through tus-js-client(I think). If information means anything, I have spent quite awhile trying every which way trying to submit the name attribute so it matches the attachment attribute.

Rails 5: Interpolated Single Quote Rendered As Unicode

Ruby version: 2.3.1
Rails version: 5.0.5
I am using Google's dataLayer to record ecommerce events in my site. We are in the middle of an upgrade from rails 4 to 5 and I'm running into a wall with one of my rails helpers. I use a helper to generate a product_list and inject it into the view so I can send it to the dataLayer.
In rails 4 the dom reflects what I write in the helper, including the quotes I need to include for the format. In rails 5 however, the quotes are being converted to unicode and I can't figure out why or how to avoid this. This is not happening when I bind on the method in the terminal, it's only happening when it is loaded in the dom. I've tried adding sanitize(), .html_safe, converting this to a hash and converting this to JSON and nothing is working.
Right now it is working on rails 4 like this:
def foo
result += "'var1':'#{item.id}',
'var2':'#{item.title}',
'var3':#{item.price}},{"
result
end
end
What I get in the DOM:
'products': [{
'var1':'result1',
'var2':'result2',
'var3': 'result3'
}]
What is being returned on the DOM in rails 5:
'products': [{
'var1':'result1',
'var2':'result2',
'var3': 'result3'
}]
Not sure where you called html_safe, I quickly added this to a view I had in Rails 5 to attempt to replicate and here are my results:
View Helper
module HomeHelper
def result
"{'var1':'test'}".html_safe
end
end
View
<h1>Home#index</h1>
<p>Find me in app/views/home/index.html.erb</p>
<%= result %>
Generated Page
<h1>Home#index</h1>
<p>Find me in app/views/home/index.html.erb</p>
{'var1':'test'}

How does one add RedCloth to a form_for?

An embaressing question, but I can't seem to translate the documentation to an actual form_for. This is all the site provides..
RedCloth.new("Some text").to_html
#=> "<p>Some text</p>"
I get that that's how I parse it after its been saved. But how do I save it as marked up text?
Here's my attempt at beginning this, but I don't know how to set the parameter to save the textarea as RedCloth. Any ideas?
- form_for #text do |f|
# some RedCloth instantiation
f.submit
You don't save the parameter parsed as RedCloth like that, nor would I recommend it. Parsing it into RedCloth will result in the original value being lost unless you stored the output in an alternate field, which is what I would recommend.
You can use a before_save in your model to parse that value and store it:
before_save :parse_text
# your model methods go here
private
def parse_text
self.parsed_text = RedCloth.new(text).to_html
end
When you want to render the parsed_text value in your view you'll have to tell Rails that it's safe by doing this:
#object.parsed_text.html_safe
However, the code contained here does not account for people mixing Markdown and HTML, so be very careful how you use it.

POSTing File Attachments over HTTP via JSON API

I have a model called Book, which has_many :photos (file attachments handled by paperclip).
I'm currently building a client which will communicate with my Rails app through JSON, using Paul Dix's Typhoeus gem, which uses libcurl.
POSTing a new Book object was easy enough. To create a new book record with the title "Hello There" I could do something as simple as this:
require 'rubygems'
require 'json'
require 'typhoeus'
class Remote
include Typhoeus
end
p Remote.post("http://localhost:3000/books.json",
{ :params =>
{ :book => { :title => "Hello There" }}})
My problems begin when I attempt to add the photos to this query. Simply POSTing the file attachments through the HTML form creates a query like this:
Parameters: {"commit"=>"Submit", "action"=>"create", "controller"=>"books", "book"=>{"title"=>"Hello There", "photo_attributes"=>[{"image"=>#<File:/var/folders/1V/1V8Kw+LEHUCKonqJ-dp3oE+++TI/-Tmp-/RackMultipart20090917-3026-i6d6b9-0>}]}}
And so my assumption is I'm looking to recreate the same query in the Remote.post call.
I'm thinking that I'm letting the syntax of the array of hashes within a hash get the best of me. I've been attempting to do variations of what I was expecting would work, which would be something like:
p Remote.post("http://localhost:3000/books.json",
{ :params =>
{ :book => { :title => "Hello There",
:photo_attributes => [{ :image => "/path/to/image/here" }] }}})
But this seems to concatenate into a string what I'm trying to make into a hash, and returns (no matter what I do in the :image => "" hash):
NoMethodError (undefined method `stringify_keys!' for "image/path/to/image/here":String):
But I also don't want to waste too much time figuring out what is wrong with my syntax here if this isn't going to work anyway, so I figured I'd come here.
My question is:
Am I on the right track? If I clear up this syntax to post an array of hashes instead of an oddly concatenated string, should that be enough to pass the images into the Book object?
Or am I approaching this wrong?
Actually, you can't post files over xhr, there a security precaution in javascript that prevents it from handling any files at all. The trick to get around this is to post the file to a hidden iframe, and the iframe does a regular post to the server, avoiding the full page refresh. The technique is detailed in several places, possibly try this one (they are using php, but the principle remains the same, and there is a lengthy discussion which is helpful):
Posting files to a hidden iframe

Resources