I have an environment variable along the lines of:
MY_VALUE: "EFINbA\u003d\u003d\n"
When I read it through ruby it is returned as:
ENV['MY_VALUE']
=> "EFINbA\\u003d\\u003d\\n"
... but only on Heroku, not on Mac (where it was set through a local_env.yml file, admittedly)
So first of all, I just don't understand why it is doing that.
Secondly, when I attempt to remove the \ and replace them with \, I have found nothing that works.
Although:
ENV['MY_VALUE'].gsub("\","x")
=> "EFINbAxu003dxu003dxn"
This:
ENV['MY_VALUE'].gsub("\","\")
... doesn't work because the last double-quote is escaped, while:
ENV['MY_VALUE'].gsub("\\","\\")
... effectively does nothing at all.
Evidently I am missing something basic here, and it's too late in the day for me to spot it.
Thanks.
You can try YAML's unescape
require 'yaml'
def unescape(s)
YAML.load(%Q(---\n"#{s}"\n))
end
unescape(ENV['MY_VALUE'])
or if you don't bring in the yaml module you can use eval
def unescape(s)
eval %Q{"#{s}"}
end
The advantage to YAML over eval is that it is presumably safer.
YAML.safe_load sometimes changes characters in the env string - so it is not an optimal solution.
As eval is also not the solution as it is not that safe, .undump was the answer I was looking for.
Related
I am loading a YAML file in Rails 3.0.9 like this:
APP_CONFIG = YAML.load(File.read(File.expand_path('../app.yml', __FILE__)))
It loads the all of the contents like hierarchical hashes, no problem. The part I don't like is the fact that the hashes can only be accessed with single or double quotes but not a symbol.
APP_CONFIG['mailer']['username'] # works fine
APP_CONFIG[:mailer][:username] # doesn't
Any thoughts?
Try using the HashWithIndifferentAccess like
APP_CONFIG = HashWithIndifferentAccess.new(YAML.load(File.read(File.expand_path('../app.yml', __FILE__))))
An alternative solution is to have the keys which you wish to access as a symbol prepended with a colon. For example:
default: &default
:symbol: "Accessed via a symbol only"
string: "Accessed via a string only"
development:
<<: *default
test:
<<: *default
production:
<<: *default
Later you can then access these like so:
APP_CONFIG[:symbol]
APP_CONFIG['string']
Note that I am using YAML::ENGINE.yamler = "syck". Not sure if this works with psych. (Psych definitely won't support key merging as I showed in the example though.)
About using HashWithIndifferentAccess: using it has the side effect of creating duplicate keys: one for symbol access and one for string access. This might be nefarious if you pass around YAML data as arrays. Be aware of this if you go with that solution.
If you are working in Ruby on Rails, You might want to take a look at symbolize_keys(), which does exactly what the OP asked for. If the hash is deep,you can use deep_symbolize_keys(). Using this approach, the answer is
APP_CONFIG = YAML.load(File.read(File.expand_path('../app.yml', __FILE__))).deep_symbolize_keys
Psych (a.k.a. YAML) provides the keyword argument :symbolize_names to load keys as symbols. See method reference
file_path = File.expand_path('../app.yml', __FILE__)
yaml_contents = File.read(file_path)
APP_CONFIG = YAML.safe_load(yaml_contents, symbolize_names: true)
This is the same from the selected answer, but with a better syntax:
YAML.load(File.read(file_path)).with_indifferent_access
There is another potential answer I discovered while digging around.
You can forgo HashWithIndifferentAccess.new by instead adding this to the top of your YAML files:
--- !map:HashWithIndifferentAccess
then simply YAML.load like normal. The only trick is that rails needs to already be loaded if you are doing this in your environment for use in initializers, etc. (like I am).
Rails has a special method to symbolize keys.
You can use load_file method and get rid of File.read
Not sure if you need expand_path also, the default directory is rails root.
I'd write it that simple:
YAML::load_file('app.yml').symbolize_keys
Just use appropriate option in your YAML parser. For instance, symbolize_names in Psych:
APP_CONFIG = YAML.load(File.read(File.expand_path('../app.yml', __FILE__)), symbolize_names: true)
See RDoc: https://ruby-doc.org/stdlib-2.6.1/libdoc/psych/rdoc/Psych.html#method-c-load.
If you're using pure Ruby (i.e. no Rails), you could intermediately change to JSON format. The JSON lib's parse method can symbolize keys.
http://ruby-doc.org/stdlib-2.0.0/libdoc/json/rdoc/JSON.html#method-i-parse
Here's what I mean:
JSON.parse(JSON.dump(YAML.load_file(File.expand_path('../app.yml', __FILE__))), symbolize_names: true)
Note: This adds overhead of conversion to and from json.
You are probably used to the params hash in Rails, which is actually a HashWithIndifferentAccess rather than a standard ruby Hash object. This allows you to use either strings like 'action' or symbols like :action to access the contents.
With a HashWithIndifferentAccess, you will get the same results regardless of what you use, but keep in mind this only works on HashWithIndifferentAccess objects.
So to make this work with YAML, you'll have to load the result of YAML.load into a HashWithIndifferentAccess, like so:
APP_CONFIG = HashWithIndifferentAccess.new( YAML.load(File.read(File.expand_path('../app.yml', __FILE__))) )
I usually don't use HashWithIndifferentAccess just to avoid confusion and prevent inconsistencies in the way that it is accessed, so what I would do instead is tack on a .deep_symbolize_keys to get the whole thing in symbol key form.
I want to include a hash and list inside a YAML file that I'm parsing with the following command:
APP_CONFIG = YAML.load_file("#{RAILS_ROOT}/config/config.yml")
My YAML file looks like this:
feeds: [{:url => 'http://www.google.com', :label => 'default'}]
But this doesn't seem to work.
How would I go about achieving such a thing?
Thanks,
Yuval
EDIT: Sorry, guys. I'm still unclear about how to do this and I suspect it is in part due to my somewhat vague phrasing. I asked a better-phrased, more broad question here. Thank you!
You can mark it up like this
feeds:
-
url: 'http://www.google.com'
label: 'default'
Note the spacing is important here. "-" must be indented by a single space (not a tab), and followed by a single space. And url & label must be indented by two spaces (not tabs either).
Additionally this might be helpful: http://www.yaml.org/YAML_for_ruby.html
Ceilingfish's answer is maybe technically correct, but he recommends to use a white space at the end of a line. This is prone to errors and is not a good practice!
This is how I would do it:
Create a settings.yaml file with the following contents:
---
feeds:
:url: 'http://www.google.com'
:label: 'default'
This will create the following hash after the YAML file has been loaded:
irb(main):001:0> require 'yaml'
=> true
irb(main):002:0> YAML.load_file('settings.yaml')
=> {"feeds"=>{:url=>"http://www.google.com", :label=>"default"}}
irb(main):003:0>
In this example, I also use symbols since this seems to be the preferred way of storing Ruby keys in Ruby.
Old question, but since I was in a similar spot... Like Jasper pointed out, Ceilingfish's answer is correct. But you can also do
feeds:
- url: 'http://www.google.com'
label: 'default'
to avoid having to rely on trailing whitespace after the dash.
Been playing with Ruby on Rails for awhile and decided to take a look through the actual source. Grabbed the repo from GitHub and started looking around. Came across some code that I am not sure what it does or what it references.
I saw this code in actionmailer/test/abstract_unit.rb
root = File.expand_path('../../..', __FILE__)
begin
require "#{root}/vendor/gems/environment"
rescue LoadError
$:.unshift("#{root}/activesupport/lib")
$:.unshift("#{root}/actionpack/lib")
end
lib = File.expand_path("#{File.dirname(__FILE__)}/../lib")
$:.unshift(lib) unless $:.include?('lib') || $:.include?(lib)
require 'rubygems'
require 'test/unit'
require 'action_mailer'
require 'action_mailer/test_case'
Can someone tell me what the $: (a.k.a. "the bling") is referencing?
$ identifies a global variable, as opposed to a local variable, #instance variable, or ##class variable.
Among the language-supplied global variables are $:, which is also identified by $LOAD_PATH
$: is the global variable used for looking up external files.
From http://www.zenspider.com/Languages/Ruby/QuickRef.html#18
$: Load path for scripts and binary modules by load or require.
I wanna note something weird about Ruby!
$ does indeed mean load path. And ; means "end line". But!
$; means field separator. Try running $;.to_s in your REPL and you'll see it return ",". That's not all! $ with other suffixes can mean many other things.
Why? Well, Perl of course!
To quote the Ruby Forum:
ruby comes with a set of predefined variables
$: = default search path (array of paths)
__FILE__ = current sourcefile
if i get it right (not 100% sure) this adds the lib path to this array
of search paths by going over the current file. which is not exactly the
best way, i would simply start with RAILS_ROOT (at least for a rails
project)
$:.unshift
is the same as
$LOAD_PATH.unshift
. You can also say:
$: <<
$LOAD_PATH <<
They are pretty common Ruby idioms to set a load path.
I'm messing around with rails 2.3 templates and want to be able to use the app name as a variable inside my template, so when I use...
rails appname -m path/to/template.rb
...I want to be able to access appname inside template.rb. Anyone know how to do this?
Thanks
I was looking for an answer to this question. unfortunately the answer above (#root) doesn't seem to work in Rails 3.
Here's the variables you can access in Rails 3 app templates (even easier):
#app_name
#app_path
Thanks for the answers. Mike Woodhouse, you were so close. Turns out, all you need to do to access the appname from inside your rails template is...
#root.split('/').last
The #root variable is the first thing created when initializing templates and is available inside your rails templates. RAILS_ROOT does not work.
In Rails 3, use the app_name attribute.
See the documentation for the Rails::Generators::AppGenerator.
I ran into a similar problem, none of the variables listed above were available to me in Rails 4. I found that #name was available while running
rails plugin new engines/dummy -m my_template.rb
There are other useful variables available from within the template. You can see for yourself and play around by utilizing pry. Inside my template I added
require 'pry'; binding.pry
and then ran ls to show a list of available instance variables
ls -i
instance variables:
#_initializer #app_path #behavior #destination_stack #extra_entries #name #output_buffer #shell
#_invocations #args #builder #dummy_path #gem_filter #options #rails_template #source_paths
#after_bundle_callbacks #author #camelized #email #in_group #original_name #shebang
There's probably a more straightforward way, but this seems to work:
RAILS_ROOT.split('/').last
EDIT: Bleah - this got voted down once, and the voter was right. If I'd read the question more carefully, I'd have noticed the 2.3 and template.rb elements. Apologies.
I suspect that RAILS_ROOT won't have been created at the point that you need the app name. Looking at ruby\lib\ruby\gems\1.8\gems\rails-2.2.2\bin\rails, however, almost the first thing that happens is this:
app_path = ARGV.first
It's used at the end of the script to allow a chdir and freeze to be done if needed - I didn't know I could insta-freeze at creation, so I learned something new at least. ARGV then gets used here:
Rails::Generator::Scripts::Generate.new.run(ARGV, :generator => 'app')
which quickly gets us to the place where ARGV is really handled:
rails-2.3.1\lib\rails_generator\scripts.rb
where I see
Rails::Generator::Base.instance(options[:generator], args, options).command(options[:command]).invoke!
Somewhere below here is probably where the templating gets handled. I'm afraid I'm at a very early stage with 2.3 and templating is an area that I haven't looked at yet.
Does that help any better than my first effort?
RAILS_ROOT will give you the absolute path to your root directory. Your app name will be the portion of the string after the final '/' which you can grab in any number of ways.
EDIT: Not quite enough to get the job done. Mike and Dan iron it out below.
I believe the preferred way now is to call Rails.root and no longer RAILS_ROOT. Apparently someone on planet rails has an aversion to uppercase or some similar important reason. As of 2.3.5 they both appear to work.
I was getting error
`template': undefined local variable or method `app_name'
ruby 1.9.2p290, rails 3.2.11, thor 0.18.0, Windows
but with rails 2.3 generator:
class DynanavGenerator < Rails::Generators::Base
(can't be sure whether this error happened under rails 3.0.9 or earlier)
changed class definition to be:
class DynanavGenerator < Rails::Generators::NamedBase
which then gave:
No value provided for required arguments 'name'
I then added a 'name' ("something" below):
rails generate dynanav something --force
which gave the original error, so I then added:
def app_name
#name.titleize
end
to the class and all was well.
As of Rails 4 (maybe earlier versions?), use Rails.application.class to get the application name. For example, if your app is named Fizzbuzz, here are a few ways you might access it:
rails(development)> Rails.application.class
=> Fizzbuzz::Application
rails(development)> Rails.application.class.name
=> "Fizzbuzz::Application"
rails(development)> Rails.application.class.parent
=> Fizzbuzz
rails(development)> Rails.application.class.parent.to_s
=> "Fizzbuzz"
I'm currently working on a largish Ruby on Rails project. It's old enough and big enough that it's not clear if all views are actually in use.
Is there any script/plugin out there that can generate a list of unused view files?
Take a look at the following script on GitHub http://github.com/vinibaggio/discover-unused-partials
I wrote a script to find unused partials/views. I assumed, though, that "unused" means that a view-file is present for which no controller-method is defined (any more). The script does not check whether the view is called because there is no link from the default-route to it. This would have been far more complex.
Place the following script in the application's script folder:
#!/usr/bin/env ruby
require 'config/environment'
(Dir['app/controllers/*.rb'] - ['app/controllers/application.rb']).each do |c|
require c
base = File.basename(c, '.rb')
views = Hash.new
Dir["app/views/#{base.split('_')[0]}/*"].each do |v|
views.store(File.basename(v).split('.')[0], v)
end
unused_views = views.keys - Object.const_get(base.camelcase).public_instance_methods - ApplicationController.public_instance_methods
puts "Unused views for #{base.camelcase}:" if unused_views.size > 0
unused_views.each { |v| puts views[v] }
end
It is kinda hackish and unfinished, but it does the job - at least for me.
Execute it like this (you only need to change the execute-bit the first time with chmod):
chmod +x script/script_name
./script/script_name
Enjoy!
Just install and run the discover-unused-partials gem:
gem install discover-unused-partials
discover-unused-partials rails_root_directory
Iterate through your partials, grep (or awk) the project for the name of the file. Adjust your search regex to look for "render :partial" at beginning of line for generic partials (eg, "_form").