Rails helper in Middleman `no implicit conversion of Symbol into String` - ruby-on-rails

I'm porting the Gulp Starter Rails helpers to Middleman but am getting the following error:
no implicit conversion of Symbol into String related to Ruby /middleman-gulp-starter/helpers/gulp_asset_helper.rb: in join, line 14
I'm not sure of the differences between Rails and Middleman to understand why this isnt working.
module GulpAssetHelper
def gulp_asset_path(path, type = nil)
rev_manifest = nil
# In development, check for the manifest every time
if !config[:build]
rev_manifest = JSON.parse(File.read(REV_MANIFEST_PATH)) if File.exist?(REV_MANIFEST_PATH)
# In production, use the manifest cached in initializers/gulp.rb
else
rev_manifest = REV_MANIFEST if defined?(REV_MANIFEST)
end
root = GULP_CONFIG['root']['dest'].gsub(/(.*)build\//, '/')
asset_path = type ? File.join(GULP_CONFIG['tasks'][type]['dest'], path) : path # LINE 14
asset_path = rev_manifest[asset_path] if rev_manifest
asset_path = File.join(root, asset_path)
File.absolute_path(asset_path, '/')
end
def gulp_js_path(path)
gulp_asset_path(path, 'js')
GULP_CONFIG
end
def gulp_css_path(path)
gulp_asset_path(path, 'css')
end
def gulp_image_path(path)
gulp_asset_path(path, 'images')
end
def sprite(id, classes = "", viewBox = "0 0 24 24")
"<svg class='sprite -#{id} #{classes}' aria-hidden='true' preserveAspectRatio viewBox='#{viewBox}'><use xlink:href='#{gulp_image_path('sprites.svg')}##{id}' /></use></svg>".html_safe
end
end
The rev and config import file:
GULP_CONFIG = JSON.parse(File.read('gulpfile.js/config.json'))
REV_MANIFEST_PATH = File.join(GULP_CONFIG['root']['dest'], 'rev-manifest.json')
if File.exist?(REV_MANIFEST_PATH)
REV_MANIFEST = JSON.parse(File.read(REV_MANIFEST_PATH))
end
Example rev-manifest.json file:
{
"images/middleman-logo.svg": "images/middleman-logo-2e3d8b5ad1.svg",
"javascripts/all.js": "javascripts/all-92681c51e741e0e1370c.js",
"stylesheets/site.css": "stylesheets/site-9b25f1d1ac.css"
}
I've output the contents of the gulpfile so know that it is being read correctly.
You can find the full repo here: https://github.com/craigmdennis/middleman-gulp-starter/tree/4_asset-helpers

it looks like it fails on this line:
asset_path = type ? File.join(GULP_CONFIG['tasks'][type]['dest'], path) : path
The File.join method expect strings so either the GULP_CONFIG['tasks'][type]['dest'] or path is not a string but a symbol. Try something like this:
asset_path = type ? File.join(GULP_CONFIG['tasks'][type.to_s]['dest'].to_s, path.to_s) : path.to_s

Related

Generating google directions in Rails

I have a method CustomGoogleDirections
class CustomGoogleDirections < GoogleDirections
##base_url = "https://maps.googleapis.com/maps/api/directions/xml"
end
Which i call in my controller to find the location based on a hash:
#locations = #all_locations.each_cons(2).with_index.with_object({}) do |((e1, e2), i), h|
h[i + 1] =
[e1, e2,
CustomGoogleDirections.new(e1, e2, key: Figaro.env.google_translate_secret_key).distance_in_miles, CustomGoogleDirections.new(e1, e2, key: Figaro.env.google_translate_secret_key).drive_time_in_minutes.url]
end
This result in the error:
Errno::ENOENT (No such file or directory # rb_sysopen - https://maps.googleapis.com/maps/api/directions/xml?destination=60%2BBarron%2BRoad%252C%2BDubai%252C%2BDubai&key=######################&origin=60%2BAlFhaged%2BRoad%252C%2BDubai%252C%2Dubai)
It used to work well before I did a rails upgrade.
Does anynone knows what I am missing please?
(When I used the generated xml link, it does load properly with all the long/lat informations)
I found a solution by modifying the gem, changing :
#url = ##base_url + '?' + #options.to_query
#xml = open(#url).read
#doc = Nokogiri::XML(#xml)
#status = #doc.css('status').text
to :
#url = ##base_url + '?' + #options.to_query
#response = Net::HTTP.get_response(URI(#url))
#doc = Nokogiri::XML(#response.body)
#status = #doc.css('status').text
It works locally but not sure about this as the right approach.

Access ActiveStorageBlob or ActiveStorageAttachment like it would be a native model

Would it be possible to access the ActiveStorageBlob or ActiveStorageAttachment like it would be a native model ?
E.g.
I want to do ActiveStorageBlob.first to access the first record of this model/table.
or. ActiveStorageAttachment.all.as_json to generate json formated print.
The background idea is to find a way how to dump the content of these ActiveStorage related tables as json formated files. Then change simething on these files, and load it back.
----Extending this text after got correct answer-----
Thank you very much Sarah Marie.
And I hope you know how to load the JSON data back into these tables ?
I have tried this :
dump_file_path = File.join(Rails.root, "backup", active_storage_blobs_file)
load_json = JSON.parse(File.read(dump_file_path))
load_json.each do |j|
ActiveStorage::Blob.create(j)
end
But thats not working.
ActiveModel::UnknownAttributeError (unknown attribute
'attachable_sgid' for ActiveStorage::Blob.)
ActiveStorage::Blob.first
ActiveStorage::Attachment.all.as_json
---- For second extended question ----
ActiveStorage::Blob.create_before_direct_upload!(
filename: j[:filename],
content_type: j[:content_type],
byte_size: j[:byte_size],
checksum: j[:checksum]
)
# or
ActiveStorage::Blob.create_before_direct_upload!(**j.symbolize_keys)
Reference: https://github.com/rails/rails/blob/5f3ff60084ab5d5921ca3499814e4697f8350ee7/activestorage/app/controllers/active_storage/direct_uploads_controller.rb#L8-L9
https://github.com/rails/rails/blob/098fd7f9b3d5c6f540911bc0c17207d6b48d5bb3/activestorage/app/models/active_storage/blob.rb#L113-L120
Now I have a complete solution, how to dump and load the ActiveStorage tables as JSON files.
...dump it
active_storage_blobs_file = "active_storage_blob.json"
active_storage_attachments_file = "active_storage_attachment.json"
puts("...dump active_storage_blob")
dump_file_path = File.join(Rails.root, "backup",active_storage_blobs_file)
dump_file = File.open(dump_file_path, "w")
dump_file.write(JSON.pretty_generate(ActiveStorage::Blob.all.as_json))
dump_file.close()
puts("...dump active_storage_attachment")
dump_file_path = File.join(Rails.root, "backup",
active_storage_attachments_file)
dump_file = File.open(dump_file_path, "w")
dump_file.write(JSON.pretty_generate(ActiveStorage::Attachment.all.as_json))
dump_file.close()
...load it back
puts("...load active_storage_blob")
dump_file_path = File.join(Rails.root, "backup", active_storage_blobs_file)
abort("File does not exist (" + dump_file_path + ") > abort <") unless File.exist?(dump_file_path)
load_json = JSON.parse(File.read(dump_file_path))
load_json.each do |j|
j = j.except("attachable_sgid")
result = ActiveStorage::Blob.create(j)
if (not result.errors.empty?)
puts(result.errors.full_messages.to_s)
puts(j.inspect)
exit(1)
end
end
puts("...load active_storage_attachment")
dump_file_path = File.join(Rails.root, "backup", active_storage_attachments_file)
abort("File does not exist (" + dump_file_path + ") > abort <") unless File.exist?(dump_file_path)
load_json = JSON.parse(File.read(dump_file_path))
load_json.each do |j|
result = ActiveStorage::Attachment.create(j)
if (not result.errors.empty?)
puts(result.errors.full_messages.to_s)
puts(j.inspect)
exit(1)
end
end

Optional method arguments

I have two methods that do almost the same thing, but but with a little difference in passing parameters:
def generate_file(filename)
draw
FileUtils.mkdir_p 'tmp/pdf'
#pdf.render_file "#{Rails.root}/tmp/pdf/#{filename}"
end
def generate_pdf(report, version)
draw
path = "tmp/pdf/reports/#{report.reference}"
FileUtils.mkdir_p(path)
#pdf.render_file "#{Rails.root}/#{path}/#{version}"
end
I want to refactor it, and use just the generate_file method when I call a function that generates pdf files. Should I pass an optional params (version = nil) and test if it's defined or not?
Like this:
def generate_file(filname, version = nil, report = nil)
draw
if report && version
path = "tmp/pdf/reports/#{report.reference}"
FileUtils.mkdir_p(path)
#pdf.render_file "#{Rails.root}/#{path}/#{version}"
else
FileUtils.mkdir_p 'tmp/pdf'
#pdf.render_file "#{Rails.root}/tmp/pdf/#{filename}"
end
end
It looks like you have some typos (filname instead of filename) and syntax errors (e.g., if..end..else..end) in the proposed code. How about something more like:
def generate_file(filename, version=nil, report=nil)
draw
report_version = report && version
path = report_version ? "tmp/pdf/reports/#{report.reference}" : "temp/pdf"
FileUtils.mkdir_p( path )
#pdf.render_file "#{ Rails.root }/#{path}/#{ report_version ? version : filename }"
end

Rails - Find missing keys between different locales (.yml files)

I have 2 locale files en.yml and pt.yml. There are some keys that exist only on pt.yml and other keys that exist only on en.yml
Is there a method or routine to list all these keys? (Just comparing the two files)
Example output:
en.activerecord.attributes.person.hand
pt.activerecord.models.bird
Obs: something other than the i18n-tasks missing task.
I find this solution work perfect. It's from a blog post of Kisko Labs. Reference is here: http://blog.kiskolabs.com/post/908453942/comparing-rails-locale-files-for-missing
LOCALE_1 = "~/Code/project/config/locales/fi.yml"
LOCALE_2 = "~/Code/project/config/locales/en.yml"
require 'yaml'
def flatten_keys(hash, prefix="")
keys = []
hash.keys.each do |key|
if hash[key].is_a? Hash
current_prefix = prefix + "#{key}."
keys << flatten_keys(hash[key], current_prefix)
else
keys << "#{prefix}#{key}"
end
end
prefix == "" ? keys.flatten : keys
end
def compare(locale_1, locale_2)
yaml_1 = YAML.load(File.open(File.expand_path(locale_1)))
yaml_2 = YAML.load(File.open(File.expand_path(locale_2)))
keys_1 = flatten_keys(yaml_1[yaml_1.keys.first])
keys_2 = flatten_keys(yaml_2[yaml_2.keys.first])
missing = keys_2 - keys_1
file = locale_1.split('/').last
if missing.any?
puts "Missing from #{file}:"
missing.each { |key| puts " - #{key}" }
else
puts "Nothing missing from #{file}."
end
end
This would do it:
require 'set'
require 'yaml'
files = ['en.yml', 'pt.yml']
p files.map {| file_path| YAML.load(File.read(file_path))}
.map {|object| Set.new(object.keys) }
.reduce(:^)
Docs: YAML and Set

Rspec Ruby on Rails Test File System in model

I have a model that has a method that looks through the filesystem starting at a particular location for files that match a particular regex. This is executed in an after_save callback. I'm not sure how to test this using Rspec and FactoryGirl. I'm not sure how to use something like FakeFS with this because the method is in the model, not the test or the controller. I specify the location to start in my FactoryGirl factory, so I could change that to a fake directory created by the test in a set up clause? I could mock the directory? I think there are probably several different ways I could do this, but which makes the most sense?
Thanks!
def ensure_files_up_to_date
files = find_assembly_files
add_files = check_add_assembly_files(files)
errors = add_assembly_files(add_files)
if errors.size > 0 then
return errors
end
update_files = check_update_assembly_files(files)
errors = update_assembly_files(update_files)
if errors.size > 0 then
return errors
else
return []
end
end
def find_assembly_files
start_dir = self.location
files = Hash.new
if ! File.directory? start_dir then
errors.add(:location, "Directory #{start_dir} does not exist on the system.")
abort("Directory #{start_dir} does not exist on the system for #{self.inspect}")
end
Find.find(start_dir) do |path|
filename = File.basename(path).split("/").last
FILE_TYPES.each { |filepart, filehash|
type = filehash["type"]
vendor = filehash["vendor"]
if filename.match(filepart) then
files[type] = Hash.new
files[type]["path"] = path
files[type]["vendor"] = vendor
end
}
end
return files
end
def check_add_assembly_files(files=self.find_assembly_files)
add = Hash.new
files.each do |file_type, file_hash|
# returns an array
file_path = file_hash["path"]
file_vendor = file_hash["vendor"]
filename = File.basename(file_path)
af = AssemblyFile.where(:name => filename)
if af.size == 0 then
add[file_path] = Hash.new
add[file_path]["type"] = file_type
add[file_path]["vendor"] = file_vendor
end
end
if add.size == 0 then
logger.error("check_add_assembly_files did not find any files to add")
return []
end
return add
end
def check_update_assembly_files(files=self.find_assembly_files)
update = Hash.new
files.each do |file_type, file_hash|
file_path = file_hash["path"]
file_vendor = file_hash["vendor"]
# returns an array
filename = File.basename(file_path)
af = AssemblyFile.find_by_name(filename)
if !af.nil? then
if af.location != file_path or af.file_type != file_type then
update[af.id] = Hash.new
update[af.id]['path'] = file_path
update[af.id]['type'] = file_type
update[af.id]['vendor'] = file_vendor
end
end
end
return update
end
def add_assembly_files(files=self.check_add_assembly_files)
if files.size == 0 then
logger.error("add_assembly_files didn't get any results from check_add_assembly_files")
return []
end
asm_file_errors = Array.new
files.each do |file_path, file_hash|
file_type = file_hash["type"]
file_vendor = file_hash["vendor"]
logger.debug "file type is #{file_type} and path is #{file_path}"
logger.debug FileType.find_by_type_name(file_type)
file_type_id = FileType.find_by_type_name(file_type).id
header = file_header(file_path, file_vendor)
if file_vendor == "TBA" then
check = check_tba_header(header, file_type, file_path)
software = header[TBA_SOFTWARE_PROGRAM]
software_version = header[TBA_SOFTWARE_VERSION]
elsif file_vendor == "TBB" then
check = check_tbb_header(header, file_type, file_path)
if file_type == "TBB-ANNOTATION" then
software = header[TBB_SOURCE]
else
software = "Unified"
end
software_version = "UNKNOWN"
end
if check == 0 then
logger.error("skipping file #{file_path} because it contains incorrect values for this filetype")
asm_file_errors.push("#{file_path} cannot be added to assembly because it contains incorrect values for this filetype")
next
end
if file_vendor == "TBA" then
xml = header.to_xml(:root => "assembly-file")
elsif file_vendor == "TBB" then
xml = header.to_xml
else
xml = ''
end
filename = File.basename(file_path)
if filename.match(/~$/) then
logger.error("Skipping a file with a tilda when adding assembly files. filename #{filename}")
next
end
assembly_file = AssemblyFile.new(
:assembly_id => self.id,
:file_type_id => file_type_id,
:name => filename,
:location => file_path,
:file_date => creation_time(file_path),
:software => software,
:software_version => software_version,
:current => 1,
:metadata => xml
)
assembly_file.save! # exclamation point forces it to raise an error if the save fails
end # end files.each
return asm_file_errors
end
Quick answer: you can stub out model methods like any others. Either stub a specific instance of a model, and then stub find or whatever to return that, or stub out any_instance to if you don't want to worry about which model is involved. Something like:
it "does something" do
foo = Foo.create! some_attributes
foo.should_receive(:some_method).and_return(whatever)
Foo.stub(:find).and_return(foo)
end
The real answer is that your code is too complicated to test effectively. Your models should not even know that a filesystem exists. That behavior should be encapsulated in other classes, which you can test independently. Your model's after_save can then just call a single method on that class, and testing whether or not that single method gets called will be a lot easier.
Your methods are also very difficult to test, because they are trying to do too much. All that conditional logic and external dependencies means you'll have to do a whole lot of mocking to get to the various bits you might want to test.
This is a big topic and a good answer is well beyond the scope of this answer. Start with the Wikipedia article on SOLID and read from there for some of the reasoning behind separating concerns into individual classes and using tiny, composed methods. To give you a ballpark idea, a method with more than one branch or more than 10 lines of code is too big; a class that is more than about 100 lines of code is too big.

Resources