Why does Guard run ALL my RSpec specs on every change? - ruby-on-rails-3

This testing stack is behaving unreliably. Sometimes when I run guard, it performs adequately with the guard-rspec gem and will only run the file that has been changed. At least, if the test fails it will stop with that single modified file.
But if that test passes, all bets are off. It will continue to run ALL tests when a single file is changed. On a good-sized application, this is extremely impractical and frustrating to see all tests run every time a file is saved.
My Guardfile is as follows. Notice that I had to change where it was pointing to RSpec specs. By default it thinks you are going to have a specs directory, and that all of your specs will be all jumbled into that one directory. I don't know anybody that tests like that, so it's a funny default to include. It should be looking for subdirectories under specs.
# A sample Guardfile
# More info at https://github.com/guard/guard#readme
guard 'spork', :cucumber => false, :rspec_env => { 'RAILS_ENV' => 'test' } do
watch('config/application.rb')
watch('config/environment.rb')
watch('config/environments/test.rb')
watch(%r{^config/initializers/.+\.rb$})
watch('Gemfile')
watch('Gemfile.lock')
watch('spec/spec_helper.rb') { :rspec }
end
guard 'rspec', :version => 2, :cli => "--drb" do
watch(%r{^spec/(.*)/(.+)_spec\.rb$})
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
watch('spec/spec_helper.rb') { "spec" }
# Rails examples
watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
watch(%r{^app/(.*)(\.erb|\.haml)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
watch('config/routes.rb') { "spec/routing" }
watch('app/controllers/application_controller.rb') { "spec/controllers" }
# Capybara features specs
watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/features/#{m[1]}_spec.rb" }
watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/requests/#{m[1]}_spec.rb" }
# Turnip features and steps
watch(%r{^spec/acceptance/(.+)\.feature$})
watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' }
end

The guard-rspec gem has a parameter which controls whether all tests are run after a successful test. It defaults to true, but can be changed to false to get the behavior you're looking for.
See the :all_after_pass option at https://github.com/guard/guard-rspec

For the sake of reference, the all_on_start: false and all_after_pass: false options need to be set for each guard block. It took me awhile to finally figure out why mine behaving the same way.

Related

dynamic vue-router base on webpack applications

Good day.
Problem definition:
I would like to deploy my Vue app, that utilizes vue-router, on dynamic path, which should be controlled by the WebServer, utilize vue-router in history mode, and avoid re-packaging the application for each Deployment.
F.e.
Run same app at
http://localhost/subpath/index.html
and
http://localhost/another-subpath/index.html
As vue-router configuration is done at the packaging stage (f.e. by webpack), and thus is not designed to be controlled after the packaging stage, making this simple but so-common setup not viable.
Also, vue-router have quite a complicated lifecycle, which does not allow to easily override that base setting at the application-level.
In addition - webpack ends up hardcoding actual resources into the html body, which ensures their proper consumption by the Client at the load-time, but as chunks are inter-dependent - they are almost impossible to be injected/edited dynamically. Application integrity fails in case of any modification to those on-post-DomReady event.
Research:
My search so far have not yielded any viable options to set such configuration up.
I came up with a couple of viable solutions.
One of the problems is that changing the Router Base is not the only thing one needs, in order to dynamically change the App's root, and make it function properly on dynamic URL.
In case of vue+webpack - actual scripts are added to the index.html on-build-stage, and thus - again - end up being hardcoded.
There are few options of fixing that, which I came up with.
The Ugly solution:
Regex-replace resources URLs at the index.html, on the web-server level, and hardcoded setting for your vue-router-base.
This is very questionable approach, but viable.
Unfortunately I do not have examples of that approach left, so - I can't provide any, but this should be pretty straightforward:
On your servlet-side, Pseudocode:
let fileContent = getFileContent(requested_file_name);
if (servingFile == "index.html") {
fileContent = fileContent.regexReplace("http://url_hardcoded_at_packaging_stage", "http://url_application_is_deployed_at");
fileContent = fileContent.regexReplace("setting_token_in_your_index_html", "http://url_application_is_deployed_at");
}
return fileContent;
In addition to that you will need to add this setting_token_in_your_index_html in your index.html:
<head>
<script type="text/javascript">
window.my_app_settings = {routerBase:"setting_token_in_your_index_html"}
</script>
</head>
And consume it at the vue-router level:
export default new Router({
mode: "history",
base: window.my_app_settings.routerBase,
routes: [...]
...
});
This approach is good in a sense that there is no need to modify anything on your vue-app level, and all the changes can be kept only on your servlet-side, whatever it is.
Also it has 0 performance impact on the vue-app itself.
Still ugly, but a lots better, from code-perspective at least:
Make vue-app base-aware.
This solution is not super-simple, but works reliably on all major web-servers and can be summarized as
Dynamically add vue-resources at the app init, based on cookie provided by the Web-Server.
This approach allows to "elegantly" modify all the resources URLs, with minimum impact on the performance-side, and keep things as dynamic as they can be.
This approach consists of few small changes on the Packaging, and the app-levels, and also relies on cookie to inform the app about custom base.
Provide the custom URL cookie from Server:
At your web-server add the cookie header (all major web-servers support such functionality):
(f.e. in Scalatra)
val contextShiftCookie = "subpath_where_ui_deployed";
val cookie = new Cookie("ui_deployment_root", contextShiftCookie );
cookie.setPath("/");
response.addCookie( cookie );
App modifications (index.html):
At the <head> section:
<script type="text/javascript">
// Build script names, for later injection.
let stringsJs = [
<% for (let js in htmlWebpackPlugin.files.js) { %>
"<%= htmlWebpackPlugin.files.js[js] %>",
<% } %>
];
let stringsCss = [
<% for (let css in htmlWebpackPlugin.files.css) { %>
"<%= htmlWebpackPlugin.files.css[css] %>",
<% } %>
];
// Simple vanilla Cookie getter (replace with something else if needed).
function getCookie(cname) {
var name = cname + "=";
var decodedCookie = decodeURIComponent(document.cookie);
var ca = decodedCookie.split(';');
for(var i = 0; i < ca.length; i++) {
var c = ca[i];
while (c.charAt(0) == ' ') {
c = c.substring(1);
}
if (c.indexOf(name) == 0) {
return c.substring(name.length, c.length);
}
}
return "";
}
// Simple vanilla onDomReady handler (replace with something else if needed).
(function(exports, d) {
function domReady(fn, context) {
function onReady(event) {
d.removeEventListener("DOMContentLoaded", onReady);
fn.call(context || exports, event);
}
function onReadyIe(event) {
if (d.readyState === "complete") {
d.detachEvent("onreadystatechange", onReadyIe);
fn.call(context || exports, event);
}
}
d.addEventListener && d.addEventListener("DOMContentLoaded", onReady) ||
d.attachEvent && d.attachEvent("onreadystatechange", onReadyIe);
}
exports.domReady = domReady;
})(window, document);
// Calculating vue router base.
let routerBase = "/";
if (getCookie("ui_deployment_root")) {
routerBase = getCookie("ui_deployment_root");
// console.log("Found base cookie", routerBase);
} else {
console.log("No base cookie found");
}
let scriptsBase = routerBase == "/"? "" : routerBase;
// Prefilling basic settings.
window.my_vue_app_config = {
routerBase: routerBase
};
function loadOneScript(){
if (window.my_vue_app_scripts.length > 0) {
let currentScript = window.my_vue_app_scripts.shift();
currentScript.onload = loadOneScript;
document.getElementsByTagName("body").item(0).append(currentScript);
}
}
// Injecting scripts using deployment context.
domReady(function(event) {
let head = document.getElementsByTagName("head").item(0);
stringsCss.forEach(css => {
let script = document.createElement("link");
script.setAttribute("rel","stylesheet");
script.setAttribute("href", scriptsBase + css);
head.append(script);
});
window.my_vue_app_scripts = [];
stringsJs.forEach(js => {
let script = document.createElement("script");
script.setAttribute("type","text/javascript");
script.setAttribute("src", scriptsBase + js);
window.my_vue_app_scripts.push(script);
});
loadOneScript();
});
</script>
Step-by-step explanation:
Consume base setting as-soon-as-it-is-available, BEFORE actual chunks are loaded, to ensure that vue-router can consume it nicely.
Gather all the script names from WebPack (see let stringsJs and let stringsCss)
Load all the css as soon as the DomReady (because css is consumed on-the-fly, and there is nothing specific about loading them. They just should be available). See stringsCss.forEach for that.
Load all the js chunks one-by-one, as this is the only way to ensure that webpack-ed app will be initialized properly (see stringsJs.forEach, and loadOneScript routine).
Scripts loading is done in such manner that each script (chunk) requests another chunk as soon as it is loaded and consumed by the Client (browser). This ensures app integrity, and proper initialization, regardless of how it was packed.
Chunks are processed in proper order, provided by the webpack, again - to ensure integrity.
vue-router changes:
Consume new setting at the router-level:
export default new Router({
mode: "history",
base: window.my_vue_app_config.routerBase,
routes: [...]
...
});
Webpack mechanism changes:
To support custom bundling at the webpack:
<% for (let js in htmlWebpackPlugin.files.js) { %>
"<%= htmlWebpackPlugin.files.js[js] %>",
<% } %>
, you will need to modify the /app/build/webpack.ENV.conf.js file:
...
new HtmlWebpackPlugin({
...
inject: false,
...
}),
Where .ENV. should be your desired bundle target, but I suggest changing this for all your environments, as then you will be sure that your index.html changes work properly on all Environments.
Performance considerations:
This was my biggest concern with this "sequential scripts injection", because it sounds very bad, from performance perspective.
But, to my biggest surprise, actual tests have shown only ~0.100-0.150ms raize in the JavaScript processing time, compared to the fully-static serve of the Application, according to tests on all major Web Clients (browsers).
This basically renders performance concern irrelevant in my case, but we wary that this might differ in your case.
Other impact on the application lifecycle is totally neglectable, at least in my case (less than 0.05ms in overall impact on the App Load).
P.S.
As this approach is something I've developed purely myself - I would appreciate constructive criticism, and improvement proposals :)

Aurelia library js file in Bundle but is resolved as static file

My project structure is as follows:
src
..lib
....someLibrary.js
bundles.js:
"bundles": {
"dist/app-build": {
"includes": [
"[**/*.js]",
"**/*.html!text",
"**/*.css!text"
],
"options": {
"sourceMaps": 'inline'
"inject": true,
"minify": true,
"depCache": true,
"rev": true
}
},
The project builds fine, but when I check app-build.js I don't find a definition for lib/someLibrary.js. I am using typescript for my own project so I assume this has something to do with that, how can I mix regular js files and output from my transpiled TS files into the same app-build bundle?
Update
So I tried to split the 'build-system' gulp task into two tasks: 'build-typescript' which is the same as 'build-system' was before, then I created 'build-libs' which looks like so:
gulp.task('build-libs', function() {
return gulp.src(paths.root + '**/*.js')
.pipe(plumber({errorHandler: notify.onError('Error: <%= error.message %>')}))
.pipe(changed(paths.output, {extension: '.js'}))
.pipe(sourcemaps.write('.', { includeContent: false, sourceRoot: '/src' }).on('error', gutil.log))
.pipe(gulp.dest(paths.output));
});
I then added to my dist/app-build bundle config: "lib/someLibrary.min.js"
And now my app-build.js does have the library defined, however when I try to use the library in one of my views using:
<require from="lib/someLibrary.min.js">
I get an error:
Failed to load resource: the server responded with a status of 404 (Static File '/dist/lib/someLibrary.min.html' not found)
What?!?? Why is it looking for html when nowhere is html ever involved in this whole scenario? Why is something that should be easy this hard?
Update2
So apparently 'require' does not work with javascript files. I changed to use the 'script' tag, however it seems these get stripped out when rendered by Aurelia. I am at a loss as to how to get Aurelia to do what I want it to.
Ok so after much frustration and disbelief at how hard something so simple could be, in addition to the changes to the build tasks mentioned above (which includes the javascript library file that has no npm/jspm package into the app-bundle) I created a modified version of this solution which looks as follows:
import { bindable, bindingMode, customElement, noView } from 'aurelia-framework';
#noView()
#customElement('scriptinjector')
export class ScriptInjector {
#bindable public url;
#bindable public isLocal;
#bindable public isAsync;
#bindable({ defaultBindingMode: bindingMode.oneWay }) protected scripttag;
public attached() {
if (this.url) {
this.scripttag = document.createElement('script');
if (this.isAsync) {
this.scripttag.async = true;
}
if (this.isLocal) {
const code = 'System.import(\'' + this.url + '\').then(null, console.error.bind(console));';
this.scripttag.text = code;
} else {
this.scripttag.setAttribute('src', this.url);
}
document.body.appendChild(this.scripttag);
}
}
public detached() {
if (this.scripttag) {
this.scripttag.remove();
}
}
}
To use it, simply add the following tag to the view where you want the script library to be used as follows:
<scriptinjector url="lib/bootstrap-toc.js" is-local.bind='true'></scriptinjector>
This will keep the original scriptinjector functionality which allows you to add remote 3rd party libraries to your Aurelia app but it will also allow you to load any local 3rd party libraries that you have bundled with your app.
Hope this helps someone.

Wicked_PDF render a string from a template in a background process

I've got a controller "tech" that has an action to email and invoice, from there we use Delayed::Job.enqueue to shove the actual email action into a background process which will be handled via a worker dyno on Heroku.
This is all working fine.
The trouble that I found is that my generated PDF invoice lives over on the Heroku Web Dyno file system and the Worker has no idea where this is.
I will do upload the PDF during the generation process, it takes too damn long.
So I need to create the invoice over on the worker dyno when it goes to execute the mailer action to send the message.
I found this blog with some detailed instructions on creating the pdf from a string: http://viget.com/extend/how-to-create-pdfs-in-rails
But it's not working at all for me, here is the code:
html = render_to_string(:action =>":show", :layout => "invoice.html")
#pdf = WickedPdf.new.pdf_from_string(html)
And the error:
"last_error"=>"undefined method `response_body=' for #<MailSenderJob:0x007fdf7e70a638>
I know this is from the docs:
WickedPdf.new.pdf_from_string(
render_to_string('templates/pdf.html.erb', :layout => 'pdfs/layout_pdf'),
:footer => {
:content => render_to_string(:layout => 'pdfs/layout_pdf')
}
)
And that code has never worked for me at all.
What I'm getting over and over is the response_body= error. It's like it's not getting a response at all.
At the top of my file I'm doing:
include ActionController::Rendering
Because this is the module that has the render_to_string method inside it.
Any help at all - please keep in mind in your response that I'm running this code on a Heroku WORKER dyno - so if there's any dependency that I need to manually include that is naturally included on the web server, please let me know.
I ended up having to do some weird stuff with this to finally get it working.
html = File.read(Rails.root.join('app','views','technician','invoice.html.erb'))
html = ERB.new(html).result(binding)
html = html.gsub!(/\0/,'') # There is a null byte in the rendered html, so we'll strip it out (this is kind of a hack)
# Render the PDF - we're on a worker dyno and have no access to the pdf we rendered already on the web dyno
#pdf = WickedPdf.new.pdf_from_string(
html,
:handlers => [:erb],
:footer => {
:center => "Copyright 2014"
},
:disable_smart_shrinking => true,
)
#pdf = #pdf.gsub!(/\0/,'') # Again with the null bytes!
Using Partials.
I know what you mean, it gets a little funky when you're rendering PDFs in the background job as opposed to a Controller action.
I thought I would share my implementation as a comparison and for others to get another example from.
notification_mailer.rb
def export
header_html = render_to_string( partial: 'exports/header.pdf.erb',
locals: { report_title: 'Emissions Export' } )
body_html = render_to_string( partial: "exports/show.pdf.erb" )
footer_html = render_to_string( partial: 'exports/footer.pdf.erb' )
#pdf = WickedPdf.new.pdf_from_string(
body_html,
orientation: 'Landscape',
margin: { bottom: 20, top: 30 },
header: { content: header_html },
footer: { content: footer_html } )
# Attach to email as attachment.
attachments["Emissions Export.pdf"] = #pdf
# Send email. Attachment assigned above will automatically be included.
mail( { subject: 'Emissions Export PDF', to: 'elon#musk.com' } )
end

Assigning a class to a custom stage (Puppet)

I'm working on my first Puppet file for provisioning a Vagrant setup, and I'm sort of stuck.
I'm using the RVM module to handle Ruby and RubyGem installations, but apparently they use their own custom stage called 'rvm-install' that runs BEFORE the main stage.
In order to get the dependencies for RVM installed (Package resources), I need to run them before the 'rvm-install' stage. I realized this means I need a custom stage to have run before that.
I've written this class that encompasses the things needing done...but I don't understand how to assign the class to a stage...the documentation at PuppetLabs didn't seem to cover how you're supposed to do it when you already have a block of stuff in the class.
class before-rm {
exec { "apt-get update":
command => "/usr/bin/apt-get update"
}
package { "libxml2":
ensure => present,
require => Exec['apt-get update']
}
package { "nodejs":
ensure => present,
require => Exec['apt-get update']
}
}
Any help would be greatly appreciated. This is how I've got the Stage defined in the same file:
# Custom stage!
stage { 'before-rvm':
before => Stage['rvm-install']
}
Stage['before-rvm'] -> Stage['rvm-install']
Normally you would instantiate the before-rm class like this for the main stage:
include before-rm
which is equivalent to
class { 'before-rm': }
To instantiate a class for another stage you can use the metaparameter (not a parameter of the class, of all classes in general) stage.
class { 'before-rm':
stage => before-rvm
}
Here is a link to this in the docs: http://docs.puppetlabs.com/puppet/2.7/reference/lang_run_stages.html#assigning-classes-to-stages

Shoulda rspec matchers ensure_inclusion_of

I have a test using shoulda that is failing for reasons I don't understand. Any idea what the fix is for this? I hardcoded the array for testing purposes.
All my other shoulda matcher based tests are working fine.
Validation
validates_inclusion_of :status, :in => ["Active", "Closed"]
Test:
it { should ensure_inclusion_of(:status).in_array(["Active", "Closed"]) }
Failure
Failure/Error: it { should ensure_inclusion_of(:status).in_array(["Active", "Closed"]) }
["Active", "Closed"] doesn't match array in validation
Looking at the source code for that matcher:
https://github.com/thoughtbot/shoulda-matchers/blob/master/lib/shoulda/matchers/active_model/ensure_inclusion_of_matcher.rb#L88
Do you have another validation which prevents nil or blank values for :status?