nanoc: problems testing a compile rule by item name or identifier - ruby

I use the following tests in a nanoc rule for compiling various kinds of content (including partials) in multiple directories by matching them with their identically-named layouts.
Now I've added index files to each content dir, but these need the default layout. This obviously works fine if I add an item named 'index:' to the metadata in the 'index.md' files…
---
title: 'This page title'
index: 'y'
---
…but checking for if #item[:index] seems a bit clunky, so I've been trying (well, hacking around) to find a way to omit 'index:' from the metadata and test by nanoc rep name or identifier - see the commented-out if statements in the code below:
layouts = ['layoutone','layouttwo','layoutetc']
layouts.each do |dir|
compile "/#{dir}/*" do
# if item.identifier == "/#{dir}/index/"
# if item.identifier =~ %r{/\w/index/}
# if #item.rep_named(:index)
if #item[:index]
filter :kramdown
layout "default"
elsif #item[:inc]
filter :erb
filter :kramdown
layout "#{dir}"
else
filter :kramdown
layout "#{dir}"
end
end
end
What's wrong with the syntax/logic in my commented-out lines?
Edit:
I was missing the blindingly obvious here: simply add /content/dir_name.md at the same level as /content/dir_name/* to create /dir_name/index.html and /dir_name/*.html, and apply rules to those /content/dir_name.md files.

Did you change nanoc.yaml after nanoc create-site? Because I recall that by default, identifiers in nanoc don't contain the last index part of source file name.
Say, file content/dirA/index.markdown will have identifier /dirA/ or something, and compile to content/dirA/index.html. This may be the reason why your index regex didn't hit.
Yes, a little tricky, but nanoc is great.
update
I found a way to tell the content filename: item.raw_filename.
This document says it is only for binary files, while it also work on text files in my experiment.
# in compile block of Rules
item.identifier
# => "/aaa/"
item.raw_filename
# => "content/aaa.markdown"

Related

Keeping files updated with a Chef recipe

The challenge prompt is above, and my latest attempt is below. The directories and files are created as expected, and the read-out after executing chef-apply multipleCopies.rb tells me the files are linked, but when I update any one of the files, the others do not follow suit. Any ideas? Here is my code:
for x in 1..3
directory "multipleCopy#{x}" do
mode '0755'
action :create
end
end
file "multipleCopy1/secret.txt" do
mode '0755'
action :create
end
for x in 2..3
link "multipleCopy#{x}/secret.txt" do
to "multipleCopy1/secret.txt"
link_type :hard
subscribes :reload, "multipleCopy1/secret.txt", :immediately
end
end
Note: For less headache, I am testing the recipe locally before uploading to the ubuntu server referenced in the prompt, which is why my file paths are different and why I have not yet included the ownership properties.
So a file hard link doesn't seem to be what the question is going for (though I would say your solution is maybe better since this is really not what Chef is for, more on that later). Instead they seem to want you to have three actually different files, but sync the contents.
So first the easy parts, creating the directories and the empty initial files. It's rare to see those for loops used in Ruby code, though it is syntactically valid:
3.times do |n|
directory "/var/save/multipleCopy#{n+1}" do
owner "ubuntu"
group "root"
mode "755"
end
file "/var/save/multipleCopy#{n+1}/secret.txt" do
owner "root
group "root"
mode "755"
end
end
But that doesn't implement the hard part of sync'ing the files. For that we need to first analyze the mtimes on the files and use the most recent as the file content to set.
latest_file = 3.times.sort_by { |n| ::File.mtime("/var/save/multipleCopy#{n+1}/secret.txt") rescue 0 }
latest_content = ::File.read("/var/save/multipleCopy#{latest_file+1}/secret.txt") rescue nil
and then in the file resource:
file "/var/save/multipleCopy#{n+1}/secret.txt" do
owner "root
group "root"
mode "755"
content latest_content
end
As for this not being a good use of Chef: Chef is about writing code which asserts the desired state of the machine. In the case of files like this, rather than doing this kind of funky stuff to check if a file has been edited, you would just say that Chef owns the file content for all three and if you want to update it, you do it via your cookbook (and then usually use a template or cookbook_file resource).

IntelliJ Ruby warning "Cannot resolve properly, was not processed"

I have many lines in my specs that result in this IntelliJ warning:
"Cannot resolve properly, was not processed"
The vast majority of the lines have this format:
expect(result[:err]).to include('(Check the file permissions.)')
If I move the literal string to a separate variable, the warning goes away:
msg = '(Check the file permissions.)'
expect(result[:err]).to include(msg)
Is there a way to make this error go away (other than moving all my string literals to variables)?
My guess is that the RubyMine parser thinks that include is the Ruby keyword to include a module and so it emits a warning telling it cannot find the corresponding module.
The only way I found to fix this warning is to use the inclusion alias proposed by the rspec include matcher :
expect(result[:err]).to inclusion('(Check the file permissions.)')
This fixes the warning and the expectation works the same, but sadly the english sentence is bad.
There is also 3 other aliases available, but they don't give better english syntax:
alias_matcher :a_collection_including, :include
alias_matcher :a_string_including, :include
alias_matcher :a_hash_including, :include
alias_matcher :including, :include
These alias definitions can be found here
Maybe by chance this answer could lead someone to a better solution.
If you're willing to switch from using the word include to something like contain, you could simply create a custom matcher:
RSpec::Matchers.define :contain do |expected|
match do |actual|
expect(actual).to include(expected)
end
end
You could either add that code directly in your rails_helper.rb file, or better yet in a separate file. For instance, create spec/support/custom_matchers.rb and place the code there. You'll need to make sure that file gets included when running rspec. To do that, you could uncomment the following line which appears in the default spec/rails_helper.rb file:
# Dir[Rails.root.join('spec', 'support', '**', '*.rb')].each { |f| require f }
With that in place, your spec file would read:
expect(result[:err]).to contain('(Check the file permissions.)')
It can be fixed by adding this to rails_helper.rb or support/rubymine_stubs.rb:
# Rubymine IDE underlines `include` matchers with warning "Cannot resolve properly, was not processed"
# To fix this issue let's make an alias `contain` and use it instead
RSpec::Matchers.alias_matcher :contain, :include
module RubymineStubs
# create stub for `contain` so Rubymine won't underline it
def contain(*_args) end
end

Use issue_closing_pattern variable to close multiple issues in gitlab

I'd like to have the ability to close multiple issues with one commit by referencing multiple issues with the default pattern ^([Cc]loses|[Ff]ixes) +#\d+a. I know that this will only affect fixes #number-patterns at the beginning of lines and that's what I want.
But I wasn't yet able to get it to work.
I'm currently using Gitlab 6.1, installed it according to the installation readme on github and didn't change anything other then the codesnippet below.
Here's what I tried:
First I changed in {gitlab-directory}/app/models/commit.rb the following (original code commented out):
def closes_issues project
md = safe_message.scan(/(?i)((\[)\s*(close|fix)(s|es|d|ed)*\s*#\d+\s*(\])|(\()\s*(close|fix)(s|es|d|ed)*\s*#\d+\s*(\)))/)
#md = issue_closing_regex.match(safe_message)
if md
extractor = Gitlab::ReferenceExtractor.new
md.each do |n|
extractor.analyze(n[0])
end
extractor.issues_for(project)
#extractor = Gitlab::ReferenceExtractor.new
#extractor.analyze(md[0])
#extractor.issues_for(project)
else
[]
end
end
But the regex used in this code snippet doesn't fit my needs and isn't really correct (e.g.: (fixs #123) and (closees #123) would both work).
After testing this codesnippet and confirming that this one works with patterns that match the regex used in the snippet, I tried to change the regex. At first, I tried to do this in the second line:
md safe_message.scan(/#{Gitlab.config.gitlab.issue_closing_pattern}/)
This one didn't work. I didn't found any error messages in log/unicorn.stderr.log so I tried to use the default regex from the config file directly without variable:
md safe_message.scan(/^([Cc]loses|[Ff]ixes) +#\d+a/)
But this one didn't work, too. Again, no error messages in log/unicorn.stderr.log.
How do I use the variable issue_closing_pattern from the config file as regex pattern in this code snippet?
If the regex you provide to the String#scan method contains capture groups, it returns an array of arrays containing the patterns matched by each group:
irb(main):014:0> regex = "^([Cc]loses|[Ff]ixes) +#\\d+"
=> "^([Cc]loses|[Ff]ixes) +#\\d+"
irb(main):017:0> safe_message = "foo\ncloses #1\nfixes #2\nbar"
=> "foo\ncloses #1\nfixes #2\nbar"
irb(main):018:0> safe_message.scan(/#{regex}/)
=> [["closes"], ["fixes"]]
Because the default regex has a capture group for just the "closes/fixes" bit, that's all the loop is seeing, and those strings don't contain the issue references! To fix it, just add a capture group around the entire pattern:
irb(main):019:0> regex = "^(([Cc]loses|[Ff]ixes) +#\\d+)"
=> "^(([Cc]loses|[Ff]ixes) +#\\d+)"
irb(main):020:0> safe_message.scan(/#{regex}/)
=> [["closes #1", "closes"], ["fixes #2", "fixes"]]

How to Locate Target Directory

Does buildr have pre-defined variables, like capistrano, for directories like 'target', 'reports', etc? If not, rather than hard-coding the location of these directories, how else can we locate/determine these paths?
The end goal is to create a task that will on-the-fly create a file and insert it into the target directory.
Buildr defines symbolic names for the special directories. The path_to (aka _) method accepts these symbolic names and automatically translates them into the paths for the current layout. E.g.,
define 'foo' do
puts _(:target, :main, :classes) # => /some/root/foo/target/classes
puts path_to(:source, :main, :java) # => /some/root/foo/src/main/java
end
As Antoine noted in reply to another answer, there's a list of these symbolic names in the documentation.
You can define a new layout and use it in your project.
The example of buildfile here:
my_layout = Layout.new
my_layout[:source, :main, :java] = 'java'
my_layout[:source, :main, :resources] = 'resources'
define 'foo', :layout=>my_layout do
...
end
Update
Link to Buildr's tutorial http://buildr.apache.org/extending.html#layouts
As UR6LAD says, buildr stores all its paths in a per-project instance of Layout. This page describes the default layout.
The target directory can be accessed using layout[:target].

Nanoc layout compile Rules

I'm working with nanoc and I want my index.html to point to specific layout so I created that layout and it is called nosidebar.html
My Rules looks like:
compile 'index.html' do
layout 'nosidebar'
end
and this doesn't seem to work. What am I doing wrong?
You can always add something like:
compile '*' do
if item.binary?
# don’t filter binary items
else
layout item[:layout] || 'default'
end
end
That means you can just decide the template on the file by adding:
---
layout: nosidebar
---
at the yaml front matter of the file.
I haven't done exactly what you are but maybe something like this:
compile '/' do
rep.layout 'nosidebar'
end

Resources