Redcarpet gem not working properly? - Rails 4 - ruby-on-rails

i have installed and setup Redcarpet gem for markdown with CodeRay gem for syntax highlighting.
My problem is that the
``` ruby
```
which in markdown would provide a code block,its loaded but not styled properly,actually there is no style on the pre tags.
here is the code in my application_helper.rb
class CodeRayify < Redcarpet::Render::HTML
def block_code(code, language)
CodeRay.scan(code, language).div(:line_numbers => :table)
end
end
def markdown(text)
coderayified = CodeRayify.new(:filter_html => true, :hard_wrap => true)
options = {
fenced_code_blocks: true,
no_intra_emphasis: true,
autolink: true,
strikethrough: true,
lax_spacing: true,
superscript: true
}
markdown_to_html = Redcarpet::Markdown.new(coderayified,options)
markdown_to_html.render(text).html_safe
end
then all i do is this
in other words this styling that stackoverflows does when pressing ctrl+K is not there in my case.

There are no attributes of <pre> tag, if you mean that by saying 'no style'. I just recreated your example and I see that Ruby code is colored properly within <pre> block: keywords have their own styles, and everything like that. If you really do have an error, check your gem versions. I have coderay 1.1.0 and redcarpet 3.3.2 and everything looks fine.

Related

Dynamically render a sass file through sprockets

I want, from a helper, to render some variables in a .scss.erb template that makes use of the image-url() sass function:
// template.scss.erb
#<%= id %> {
background-image: image-url('<%= image_file %>');
}
So far, the ERB part has been easy:
(leveraging this stack overflow answer)
vars_binding = OpenStruct.new(
id: 'foo',
image_file: 'foo.jpg'
).instance_eval { binding }
template = File.read('path/to/template.scss.erb')
rendered_sass = ERB.new(template).result(vars_binding)
Running that code, sass is now equal to:
#foo {
background-image: image-url('foo.jpg');
}
However, when I next try to run:
css = Sass::Engine.new(
rendered_sass,
syntax: :scss,
cache: false,
load_paths: view_context.assets.paths,
read_cache: false,
style: :compressed
).render
It returns
NoMethodError: undefined method `[]' for nil:NilClass
from …/sprockets-3.2.0/lib/sprockets/sass_processor.rb:267:in `sprockets_context'
because the call to Sass::Engine doesn’t provide a Sprockets context.
If I remove image-url() from the .scss.erb template, and replace it with the native url(), it will then render correctly as CSS, no problem.
So how do I render this template within a sprockets context?
After digging through a similar question, and a lot of trial and error, I found my solution: I have to supply a :sprockets hash when calling Sass::Engine.new.
css = Sass::Engine.new(
rendered_sass,
syntax: :scss,
cache: false,
load_paths: view_context.assets.paths,
read_cache: false,
style: :compressed,
# The key ingredient…
sprockets: {
context: view_context,
environment: view_context.assets
}
).render
It should be noted that view_context was passed from a view file, but it could have also been ActionView::Base.new
After struggling with the same issue I have found a live example of the same functionality on a Github page, surprisingly not gemified.
https://github.com/BLauris/custom-css-for-user
There is an article of the author explaining the method on this link:
http://www.diatomenterprises.com/dynamically-compile-stylesheets-with-rails-and-sass/

Rails - How to pass Sprockets::Context in manual sass compiling

I'm using the following code snippet to manually compile a sass manifest with some variable overrides appended.
template = File.read("#{Rails.root}/app/assets/schemes/#{scheme}/css/styles.css.scss")
scheme_variables.each do |key, value|
template << "$#{key}:#{value};\n"
end
engine = Sass::Engine.new(template, {
:syntax => :scss,
:cache => false,
:read_cache => false,
:style => :compressed,
:filesystem_importer => Sass::Rails::SassImporter,
:load_paths => MyApp::Application.assets.paths,
:sprockets => {
:context => ?,
:environment => MyApp::Application.assets
}
})
output = engine.render
The Sass::Engine constructor wants a sprockets context and environment in the options hash. What do I put in for the context? The first thing I tried was...
:context => MyApp::Application.assets.context_class,
...but that gives me the following error "undefined method `font_path' for #" when it hits one of my sass asset helpers.
The second thing I tried was...
:context => ActionController::Base.helpers,
...That fixed the asset helper issue, but throws the following error "undefined method `depend_on' for #" when it tries to work through my glob imports (e.g. #import "mixins/*").
I'm using Rails 4.2 and sass-rails 5.0.3.
Any advice on this would be much appreciated. Thanks!
With using Sass::Rails::ScssTemplate you can render your sass code with this snippet:
template = '...' # Your sass code
logical_path = pathname = ''
environment = Rails.application.assets
context = environment.context_class.new(environment, logical_path, pathname)
template = Sass::Rails::ScssTemplate.new(pathname) { template }
output = template.render(context, {})
If you want to render from a file then just add its path to pathname and its asset path to logical_path.
For me it works with Rails 4.2.5.1 and sass-rails 5.0.4.
I ended up solving this in a slightly different way - using Sass::Rails::ScssTemplate's render method. Basically, I write my altered css string out to a file and pass it into the Sass::Rails::ScssTemplate constructor. I then compile and remove the temp file when it's done. This doesn't feel great, but it's working well for me.
scheme_css_dir = "#{Rails.root}/app/assets/schemes/#{scheme}/css"
css = File.read("#{scheme_css_dir}/styles.css.scss")
variables_str = ''
scheme_variables.each do |key, value|
variables_str << "$#{key}:#{value};\n"
end
css.gsub!('#import "./variables";', variables_str)
file = Tempfile.new(['styles', '.css.scss'], scheme_css_dir)
file.write(css)
file.close
abs_path = file.path
relative_path = abs_path[Rails.root.to_s.size + 1..-1]
template = Sass::Rails::ScssTemplate.new(abs_path)
environment = Evrconnect::Application.assets
context = environment.context_class.new(
:environment => environment,
:name => relative_path,
:filename => abs_path,
:metadata => {}
)
output = template.render(context)
file.unlink
To answer your original question, you need to supply either the current view_context, or create one with ActionView::Base.new.
http://apidock.com/rails/AbstractController/Rendering/view_context
In Rails 5.x, Sprockets v3.7.x
NOTE: Following method requires: Rails.application.config.assets.compile == true, i.e., set config.assets.compile = true. Check Sprockets README
I manually returned compiled source for sass/js, using the following code.
# For Scss Assets
def pdf_stylesheet_link_tag(name)
if Rails.env.development?
asset = Rails.application.assets.find_asset(name + '.scss')
raw ('<style type="text/css">' + asset.source + '</style>')
else
raw ('<style type="text/css">' + pdf_asset_contents(name, '.css') + '</style>')
end
end
# For Javascript Assets
def pdf_javascript_include_tag(name, *type)
if Rails.env.development?
if debug? # Can be used to check `debug == true` in params for current route
javascript_include_tag(name)
else
asset = Rails.application.assets.find_asset(name + '.js')
raw ('<script>' + asset.source + '</script>')
end
else
javascript_tag pdf_asset_contents(name, '.js')
end
end
Above code actually helps to dynamically pass compiled version of sass/js
to Wicked_PDF, which actually helps to load styles and js on Generated PDFs, for Dev Environments.
pdf_stylesheet_link_tag can be used as a helper for templates, in development/stage (where, config.assets.precompile == false), instead of using wicked_pdf_stylesheet_link_tag(which actually requires a path to precompiled source-file).
After overnight trial and errors, I found and want to share todays's way of doing it ("today" meaning: rails 5.2.1 and sprockets 3.7.2).
Work as expected: no need of a temp file, accept #import, allow asset path helpers.
# Compile SASS and return the resulting string
# Pass the file path and name without 'sass' extension, relative to assets/stylesheets
def compile_sass(stylesheet)
# Load file content
path = Rails.root.join 'app', 'assets', 'stylesheets', "#{stylesheet}.sass"
sass = File.read path
# Configure engine
environment = Sprockets::Railtie.build_environment Rails.application
scope = environment.context_class.new environment: environment, \
filename: "/", metadata: {}
scope.sass_config.merge! cache: false, style: :compressed
# Compile
engine = Sass::Rails::SassTemplate.new {sass}
engine.render scope
end
Other sass_config options can be found here : https://github.com/sass/ruby-sass/blob/stable/doc-src/SASS_REFERENCE.md#options
The following works with rails 5.2.0, sprockets 3.7.2, sassc-rails 1.3.0 and sassc 1.12.1:
template = '...' # Your sass code
environment = Sprockets::Railtie.build_environment(Rails.application)
engine = SassC::Rails::SassTemplate.new
engine.call(environment: environment,
filename: '/',
data: template,
metadata: {})[:data]

Redcarpet Syntax Highlighting

I'm trying to get Syntax Highlighting with Redcarpet working
My appliaction_helper.rb:
module ApplicationHelper
def markdown(text)
render_options = {
# will remove from the output HTML tags inputted by user
filter_html: true,
# will insert <br /> tags in paragraphs where are newlines
hard_wrap: true,
# hash for extra link options, for example 'nofollow'
link_attributes: { rel: 'nofollow' },
# Prettify
prettify: true
}
renderer = Redcarpet::Render::HTML.new(render_options)
extensions = {
#will parse links without need of enclosing them
autolink: true,
# blocks delimited with 3 ` or ~ will be considered as code block.
# No need to indent. You can provide language name too.
# ```ruby
# block of code
# ```
fenced_code_blocks: true,
# will ignore standard require for empty lines surrounding HTML blocks
lax_spacing: true,
# will not generate emphasis inside of words, for example no_emph_no
no_intra_emphasis: true,
# will parse strikethrough from ~~, for example: ~~bad~~
strikethrough: true,
# will parse superscript after ^, you can wrap superscript in ()
superscript: true
# will require a space after # in defining headers
# space_after_headers: true
}
Redcarpet::Markdown.new(renderer, extensions).render(text).html_safe
end
end
Although the output is displayed in a codeblock (redcarpet):
I can't find the Syntax Highlighting.
I just got into Redcarpet, someone know a solution for this?
Ok i found -> Markdown and code syntax highlighting in Ruby on Rails (using RedCarpet and CodeRay) which pretty much worked (together with some custom css for Coderay).
Gemfile:
gem 'redcarpet', github: 'vmg/redcarpet'
gem 'coderay'
Application_helper.rb
class CodeRayify < Redcarpet::Render::HTML
def block_code(code, language)
CodeRay.scan(code, language).div
end
end
def markdown(text)
coderayified = CodeRayify.new(:filter_html => true,
:hard_wrap => true)
options = {
:fenced_code_blocks => true,
:no_intra_emphasis => true,
:autolink => true,
:strikethrough => true,
:lax_html_blocks => true,
:superscript => true
}
markdown_to_html = Redcarpet::Markdown.new(coderayified, options)
markdown_to_html.render(text).html_safe
end
Here is a quick way to do it with Rouge:
require 'redcarpet'
require 'rouge'
require 'rouge/plugins/redcarpet'
class HTML < Redcarpet::Render::HTML
include Rouge::Plugins::Redcarpet # yep, that's it.
end
Of course this requires rouge to be in your Gemfile.
I don't think Redcarpet can do that. In my project I followed the Railscasts episode about Redcarpet which also tackles syntax highlighting. It makes use of Pygments and Albino.
Link to the ASCIIcast version is here.

How to migrate HAML helper to HAML 4?

Hi I'm using HAML to render my blog articles and I decided to migrate to new Ruby version, new Rails version and new HAML version. The problem is that it seems something changed and I can't identify what's wrong with my code.
Could someone explain me what needs to be changed in order to work with the new version ?
UPDATE : Realized it may be related to Redcarpet and not HAML but not sure :3
As you will see I use this custom renderer to automatically display Tweets or Spotify songs from their links.
Same for code blocks colored by CodeRay.
module Haml::Filters
require "net/https"
require "uri"
include Haml::Filters::Base
class MarkdownRenderer < Redcarpet::Render::HTML
def block_code(code, language)
CodeRay.highlight(code, language, {:line_number_anchors => false, :css => :class})
end
def autolink(link, link_type)
twitterReg = /https?:\/\/twitter\.com\/[a-zA-Z]+\/status(es)?\/([0-9]+)/
spotifyReg = /(https?:\/\/open.spotify.com\/(track|user|artist|album)\/[a-zA-Z0-9]+(\/playlist\/[a-zA-Z0-9]+|)|spotify:(track|user|artist|album):[a-zA-Z0-9]+(:playlist:[a-zA-Z0-9]+|))/
if link_type == :url
if link =~ twitterReg
tweet = twitterReg.match(link)
urlTweet = tweet[0]
idTweet = tweet[2]
begin
uri = URI.parse("https://api.twitter.com/1/statuses/oembed.json?id=#{idTweet}")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
request = Net::HTTP::Get.new(uri.request_uri)
response = http.request(request)
jsonTweet = ActiveSupport::JSON.decode(response.body)
jsonTweet["html"]
rescue Exception => e
"<a href='#{link}'><span data-title='#{link}'>#{link}</span></a>"
end
elsif link =~ spotifyReg
spotify = $1
htmlSpotify = "<iframe style=\"width: 80%; height: 80px;\" src=\"https://embed.spotify.com/?uri=#{spotify}\" frameborder=\"0\" allowtransparency=\"true\"></iframe>"
htmlSpotify
else
"<a href='#{link}'><span data-title='#{link}'>#{link}</span></a>"
end
end
end
def link(link, title, content)
"<a href='#{link}'><span data-title='#{content}'>#{content}</span></a>"
end
def postprocess(full_document)
full_document.gsub!(/<p><img/, "<p class='images'><img")
full_document.gsub!(/<p><iframe/, "<p class='iframes'><iframe")
full_document
end
end
def render(text)
Redcarpet::Markdown.new(MarkdownRenderer.new(:hard_wrap => true), :tables => true, :fenced_code_blocks => true, :autolink => true, :strikethrough => true).render(text)
end
end
Thanks for helping ;) !
It seems HAML 4 is now using Tilt as its Filter for Markdown.
I didn't mention it but I was using lazy_require prior to try to migrate my code to Ruby 2 & HAML 4 but in HAML 4 lazy_require doesn't exist anymore.
Instead you have to use remove_filter method to disable default Markdown module prior redefining your own Markdown module.
Here is a basic working code :
module Haml::Filters
include Haml::Filters::Base
remove_filter("Markdown") # Removes basic filter (lazy_require is dead)
module Markdown
def render text
markdown.render text
end
private
def markdown
#markdown ||= Redcarpet::Markdown.new Redcarpet::Render::HTML, {
autolink: true,
fenced_code: true,
generate_toc: true,
gh_blockcode: true,
hard_wrap: true,
no_intraemphasis: true,
strikethrough: true,
tables: true,
xhtml: true
}
end
end
end
I encountered another problem after solving this because I was using RedCarpet instead of Redcarpet (NameError) and had a hard time realizing it :/…

How to generate code_128 barcode for string with Barby

I'm currently using Barby gem with wrapper gem has_barcode to generate a bar code for a string I have : j5xvvcz.
Gemfile gems (mostly for those who are new to this solution)
#for barcode generation
gem "barby", "~> 0.5.0"
gem "has_barcode", "~> 0.2.0"
gem "rqrcode", "~> 0.4.2" #for qr code support
Code i have in my model
include HasBarcode
has_barcode :barcode,
:outputter => :svg,
:type => :code_93,
:value => Proc.new { |p| "#{p.number}" }
Where i render it to screen:
If I try to generate a qr_code or a code_93, it all works, but nor code_128 or code_39 work, getting a data not valid message.
My worries is that code_93 won't get recognized in some devices since it seems it is not so widely adopted (from what i read here 128 would be the best solution for this)
This seems to be something i might be doing wrong, since the code is valid for code_128 aparently as i tested it here.
Anyone knows what might be wrong with my approach?
Apparently mode 'A' for code_128 doesn't suit for smallcaps with Barby.
So i had to had mode B working.
Current published gem version of has_barcode forces mode 'A', so I added a little "home-patch", while adding a suggestion to github.
Here is my final has_barcode.rb file :
require "rubygems"
require "i18n"
require "active_support"
require "active_support/hash_with_indifferent_access.rb"
require "active_support/inflector.rb"
require "barby"
require "has_barcode/configuration"
module HasBarcode
def self.included(base)
base.send(:extend, ClassMethods)
end
module ClassMethods
def has_barcode(*args)
options = args.extract_options!
##barcode_configurations ||= {}
##barcode_configurations[args.first] = HasBarcode::Configuration.new(options)
define_method args.first do
if options[:type] == :code_128
##barcode_configurations[args.first].barcode_class.new(options[:value].call(self), options[:code128])
else
##barcode_configurations[args.first].barcode_class.new(options[:value].call(self))
end
end
define_method "#{args.first}_data" do |opts|
if opts
send(args.first).send("to_#{options[:outputter]}", opts)
else
send(args.first).send("to_#{options[:outputter]}")
end
end
end
def barcode_configurations
##barcode_configurations
end
end
end
My model :
has_barcode :barcode,
outputter: :svg,
type: :code_128,
code128: 'B',
value: Proc.new { |p| "#{p.number}" }
and my view :
<%= my_model.barcode_data(xdim:2, height:60).html_safe %>
Do notice that at current date (2012-12-05) current gem isn't updated with latest changes that allow to pass outputter arguments like xdim and height.
This answer is based on latest code updates (issue related) and my own suggestion, and can be found here
While this solution isn't embedded into the has_barcode gem, i will be using Barby directly:
In my model added :
def get_barcode(number)
require 'barby'
require 'barby/barcode/qr_code'
require 'barby/outputter/svg_outputter'
barcode = Barby::Code128B.new("#{number}")
barcode.to_svg(xdim:2, height:60)
end
in the view :
<%= my_model.get_barcode(my_model.number).html_safe %>

Resources