YAML in Ruby. What is the difference between load and load_file? - ruby-on-rails-3

I have this practice.yml file:
foo: whatever
bar:
-
fruit: apple
name: steve
sport: baseball
- more
-
python: rocks
perl: papers
ruby: scissorses
What is the difference under the hood between #load and #load_file?
#load seems to do this magic and takes a string:
pry(main)> YAML.load("'a'")
"a"
[11] pry(main)> YAML.load("a:1")
"a:1"
[12] pry(main)> YAML.load("-a")
"-a"
[13] pry(main)> YAML.load("[a]")
[
[0] "a"
]
[14] pry(main)> YAML.load("[a,bc]")
[
[0] "a",
[1] "bc"
]
Whereas load_file seems to just take a file with valid yaml inside:
irb(main):002:0> YAML.load_file("practice.yml")
=> {"foo"=>"whatever", "bar"=>[{"fruit"=>"apple", "name"=>"steve", "sport"=>"baseball"}, "more", {"python"=>"rocks", "perl"=>"papers", "ruby"=>"scissorses"}]}

load_file calls load, like this:
def self.load_file(filename, options={})
if SafeYAML::MULTI_ARGUMENT_YAML_LOAD
File.open(filename, 'r:bom|utf-8') { |f| self.load(f, filename, options) }
else
# Ruby pukes on 1.9.2 if we try to open an empty file w/ 'r:bom|utf-8';
# so we'll not specify those flags here. This mirrors the behavior for
# unsafe_load_file so it's probably preferable anyway.
self.load File.open(filename), nil, options
end
end
Now the prototype for load() is
def self.load(yaml, filename=nil, options={})
so basically load_file is opening up the file name and passing the resulting string to load(), after checking for some empty file case related to Ruby 1.9.2

Related

perl6 'do(file)' equivalent

In perl5 I used to 'do (file)' for configuration files like this:
---script.pl start ---
our #conf = ();
do '/path/some_conf_file';
...
foreach $item (#conf) {
$item->{rules} ...
...
---script.pl end ---
---/path/some_conf_file start ---
# arbitrary code to 'fill' #conf
#conf = (
{name => 'gateway',
rules => [
{verdict => 'allow', srcnet => 'gw', dstnet => 'lan2'}
]
},
{name => 'lan <-> lan2',
rules => [
{srcnet => 'lan', dstnet => 'lan2',
verdict => 'allow', dstip => '192.168.5.0/24'}
]
},
);
---/path/some_conf_file end ---
Also Larry Wall's "Programming Perl" also mentions this method:
But do FILE is still useful for such things as reading program
configuration files. Manual error checking can be done this way:
# read in config files: system first, then user
for $file ("/usr/share/proggie/defaults.rc",
"$ENV{HOME}/.someprogrc") {
unless ($return = do $file) {
warn "couldn't parse $file: $#" if $#;
warn "couldn't do $file: $!" unless defined $return;
warn "couldn't run $file" unless $return;
} }
Benefits:
does not require write your own parser each time - perl parse and
create data structures for you;
faster/simpler: native perl data
structures/types without overheads for converting from external format (like YAML);
does not require manipulate #INC to load the
module from somewhere compared to module as conf file;
less extra
code compared to modules as conf file;
"syntax" of "configuration file" is powerful as perl itself;
"ad hoc" format;
Disadvantages:
no isolation: we can execute/destroy anything from "configuration
file";
How do I get the same with perl6?
Is there way to do it better in perl6 (without Disadvantages) and without parsing own syntax, grammars, module including?
Something like "Load hashes or arrays from text representation from file"?
You can use EVALFILE($file) (ref. http://doc.perl6.org/language/5to6-perlfunc#do).
As you pointed out, using EVALFILE has disadvantages, so I'm not going to add anything in that direction :-)
Here's a sample configuration file:
# Sample configuration (my.conf)
{
colour => "yellow",
pid => $*PID,
homedir => %*ENV<HOME> ~ "/.myscript",
data_source => {
driver => "postgres",
dbname => "test",
user => "test_user",
}
}
and here's a sample script using it:
use v6;
# Our configuration is in this file
my $config_file = "my.conf";
my %config := EVALFILE($config_file);
say "Hello, world!\n";
say "My homedir is %config<homedir>";
say "My favourite colour is %config<colour>";
say "My process ID is %config<pid>";
say "My database configuration is:";
say %config<data_source>;
if $*PID != %config<pid> {
say "Strange. I'm not the same process that evaluated my configuration.";
}
else {
say "BTW, I am still the same process after reading my own configuration.";
}

How to serve the stream pdf with ring

I'm trying to serve a clj-http generated document directly via ring/compojure.
I thought ring.util/piped-output-stream would work, but it seems I'm not understanding something here...
This:
(defn laminat-pdf-t
[natno]
(piped-input-stream
(fn [output-stream])
(pdf
[ {:title (str "Omanimali-Kuscheltierpass" natno)
:orientation :landscape
:size :a6
:author "Omanimali - Stefanie Tuschen"
:register-system-fonts true
}
;; [:svg {} (clojure.java.io/file
;; (str "/einbuergern/" natno "/svg" ))]
[:paragraph "Some Text"] ]
output-stream)))
(defn laminat-pdf
"generate individualized cuddly toy passport page"
[natno]
{:headers {"Content-Type" "application/pdf"}
:body (laminat-pdf-t natno)})
leads to an empty response...
What do I need to do differently?
Thanks,
Mathias
I think you may have a bracket out of place in your code (look at the laminat-pdf-t function below - I tweaked it slightly).
Here's exactly what I did (first creating a project with leiningen 2.3.4 called pdf-play) and it displayed a PDF correctly in IE 11.0.9600.16521, Firefox 28.0 and Chrome 33.0.1750.154 (all on Windows - sorry these are the only browsers that I have installed and I don't have a Linux or Mac box but I don't think the browser makes any difference):
project.clj
(defproject pdf-play "0.1.0-SNAPSHOT"
:dependencies [[org.clojure/clojure "1.5.1"]
[compojure "1.1.6"]
[clj-pdf "1.11.15"]]
:plugins [[lein-ring "0.8.10"]]
:ring {:handler pdf-play.handler/app})
src/pdf_play/handler.clj
(ns pdf-play.handler
(:use compojure.core
ring.util.io
clj-pdf.core)
(:require [compojure.handler :as handler]
[compojure.route :as route]))
(defn laminat-pdf-t
[natno]
(piped-input-stream
(fn [output-stream]
(pdf
[{:title (str "Omanimali-Kuscheltierpass" natno)
:orientation :landscape
:size :a6
:author "Omanimali - Stefanie Tuschen"
:register-system-fonts true
}
;; [:svg {} (clojure.java.io/file
;; (str "/einbuergern/" natno "/svg" ))]
[:paragraph "Some Text"]]
output-stream))))
(defn laminat-pdf
"generate individualized cuddly toy passport page"
[natno]
{:headers {"Content-Type" "application/pdf"}
:body (laminat-pdf-t natno)})
(defroutes app-routes
(GET "/" [] (laminat-pdf 1234))
(route/resources "/")
(route/not-found "Not Found"))
(def app (handler/site app-routes))
Then started it at the command prompt like so:
lein ring server
and had a look in the browser and there was a PDF with "Some Text" printed in it.

Pretty Print JSON generated with a jbuilder template in Rails 3.2.8

Anyone have a way to pretty print JSON output from jbuilder?
I can pretty print JSON generated within a controller action with something like:
JSON.pretty_generate(some_json_object)
but once I pass off to a jbuilder template, I'm not aware of a way to have that output pretty printed.
Right now, my action method's render statement is simple:
render formats: :json
And this successfully forces a rendering with jbuilder, regardless of input format type specified (which is my desired behavior).
I found a way to do this:
json_string = render_to_string formats: :json
json_object = JSON.parse(json_string)
render :json => JSON.pretty_generate(json_object)
Again, this assumes there is a jbuilder template for this action, which will create the initial json, which gets rendered to a string, back into a json object and then passed to pretty_generate().
It's a bit circuitous, but it works. I'm of course, totally open to tighter implementations!
# config/initializers/jbuilder_prettify.rb
require "jbuilder"
class Jbuilder
##
# Allows you to set #prettify manually in your .jbuilder files.
# Example:
# json.prettify true
# json.prettify false
#
attr_accessor :prettify
alias_method :_original_target, :target!
##
# A shortcut to enabling prettify.
# Example:
# json.prettify!
#
def prettify!
#prettify = true
end
def target!
#prettify ? ::JSON.pretty_generate(#attributes) : _original_target
end
end
# app/views/api/v1/users/show.json.jbuilder
json.prettify! if %w(1 yes true).include?(params["pretty"])
json.( #user, :id, :name, :created_at, :updated_at )
https://github.com/rails/jbuilder/issues/195#issuecomment-44440569
Expanding on Blake Miller's answer...
Here is the code from the gist:
require 'multi_json'
MultiJson.use :yajl
unless Rails.env.production?
MultiJson.dump_options = {:pretty=>true}
end
I put this into a file called /config/initializers/jbuilder_prettify.rb
In order for this to work you must have the yajl-ruby gem included in your Gemfile. Note that the jbuilder github homepage mentions here how using something like yajl-ruby will speed up your json rendering.
This worked for me, while the accepted answer did not. It's also shorter!
https://gist.github.com/jmoe/02c7476adac24eddd969
require 'multi_json'
MultiJson.use :yajl
unless Rails.env.production?
MultiJson.dump_options = {:pretty=>true}
end
config/initializers/jbuilder.rb with:
class Jbuilder
def target!
::JSON.pretty_generate(#attributes)
end
end
Result, https://localhost:3000/manifest.json
{
"name": "Socializus",
"short_name": "Socializus",
"start_url": "http://localhost:3000",
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone",
"icons": [
{
"src": "/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}
I think this is simpler,
#package = Package.first
json = JSON.parse(#blog.to_json)
PP.pp(json)
{"id_to_s"=>"5222675dbc11149e3a000002",
"title"=>"Package Title",
"version"=>"0.1.1",
"comment"=>
{"user"=>"Joe",
"description"=>"Joe's comment"},
"assets"=>
[{"id_to_s"=>"522a4620fa451436f4000001",
"_type"=>"Illustration",
"start"=>0,
"stop"=>100,
"caption"=>"mountain climbing"},
{"id_to_s"=>"522a56a6fa4514523a000001",
"_type"=>"Illustration",
"start"=>200,
"stop"=>300,
"caption"=>"airport"},
{"id_to_s"=>"522a6a0ffa4514a30e000002",
"_type"=>"Illustration",
"start"=>400,
"stop"=>600,
"caption"=>"doc"},
{"id_to_s"=>"522aa46bbc1114551f000001",
"_type"=>"Illustration",
"start"=>nil,
"stop"=>nil,
"caption"=>nil},
{"id_to_s"=>"522aa47fbc1114551f000002",
"_type"=>"Illustration",
"start"=>10,
"stop"=>30,
"caption"=>"asdflkjsd"}]}
Or, the quicker one-liner,
PP.pp JSON.parse Blog.first.to_json

Given a Rails path as a string, add some parameters

Given a string containing an arbitrary path:
s = "/api/doctors/123/patients?page=4&active=true"
What's the best way to add another parameter to this path? I'm looking for something with behavior like the following:
merge_parameters s, :foo => 'bar'
# => "/api/doctors/123/patients?page=4&active=true&foo=bar"
merge_parameters s, :page => 5
# => "/api/doctors/123/patients?page=5&active=true"
Does this exist?
What I'm looking to do is to add a next link in my API for pagination, so that clients know how to get the next page of results:
{
"results": [ {...}, {...}, ... ]
"next": "/api/doctors/123/patients?page=5"
}
My hope is that I can use request.path and this method to produce the next page of results.
You can use the URI class for this.
>rails c
Loading development environment (Rails 3.2.6)
1.9.3p194 :001 > a = URI.parse("/api/doctors/123/patients?page=4&active=true")
=> #<URI::Generic:0x007fa27c33f6f0 URL:/api/doctors/123/patients?page=4&active=true>
1.9.3p194 :002 > a.path
=> "/api/doctors/123/patients"
1.9.3p194 :003 > a.query
=> "page=4&active=true"
Split the params into a hash, merge it with new values, hash to string, reassign and then uri to string or variations of.

OR query matching nil or "" with Mongoid still matches ""?

I'm trying to write a query for an embedded Mongoid::Document which finds any record where the "address" field is neither nil nor "".
Using a combination of the MongoDB documentation, this issue in the Mongoid bug reports, and the Mongoid documentation, I think that something like this should work:
scope :with_address, where("$or" => [{:address => {"$ne" => nil}}, {:address => {"$ne" => ""}}])
When I run this, the selector looks ok:
1.9.2p290 :002 > report.document.records.with_address
=> #<Mongoid::Criteria
selector: {"$or"=>[{:address=>{"$ne"=>nil}}, {:address=>{"$ne"=>""}}]},
options: {},
class: GlobalBoarding::MerchantPrincipal,
embedded: true>
But when I look at the results, they contain an entry with a blank address:
1.9.2p290 :007 > report.document.records.with_address.last
<Record _id: 4f593f245af0501074000122, _type: nil, version: 1, name: "principal contact 3", title: "", dob: nil, address: "", email: "", phone: "", fax: "">
I can't figure out if I'm doing a query wrong, if this is a bug with Mongoid, or if there is some other issue. Does anyone have experience with such a query?
in the end, this is the only way i could find that works to select records where a certain field is not nil and not blank:
scope :with_name, all_of(:name.ne => nil).all_of(:name.ne => "")
I think you're going to chuckle at this.
Neither nil nor "" is the same as saying:
Not nil and not "".
You really mean and, and that can be expressed without $and, using just:
$ne=>nil, $ne=>""
You can do the more succint:
scope :with_name, where(:name.nin => ["", nil])
See MongoDB manual.