Using previous_changes in after_save callback with rails_admin - rails-admin

I have an after_save callback that uses the previous_changes method to test for field change. It works fine when I save a record in rails console, but fails to recognise that the field has changed when I try to trigger it in rails_admin.
def mailers
if previous_changes['pending_approval'] == [false, true]
ProjectMailer.project_owner_new_project(self).deliver_later
Rails.logger.debug("Changed Pending to False!!".green)
end
Rails.logger.debug("hit project mailer callback".red)
end
In this example, when run in console (with a record that has just changed pending_approval from true to false) I get two logged messages; "changed pending to False!!" and "hit project mailer callback"
When I change the state in rails_admin, I only get the second "hit project mailer callback." This leads me to believe that rails_admin is doing something that interferes with the Dirty class handling of my record. Is there a better way to do this?

One way to do this is to change the after callback to before and use self_attribute_changed?(from: true, to: false) like this:
def mailers
if self.pending_approval_changed?(from: true, to: false)
ProjectMailer.project_owner_new_project(self).deliver_later
Rails.logger.debug("Changed Pending to False!!".green)
end
Rails.logger.debug("hit project mailer callback".red)
end
I suppose another way that would utilize the same strategy would be to use the around_save like so
def mailers
changed = self.pending_approval.changed?(from: true, to: false)
yield
if changed
ProjectMailer.project_owner_new_project(self).deliver_later
Rails.logger.debug("Changed Pending to False!!".green)
end
Rails.logger.debug("hit project mailer callback".red)
end
I tried the first way and it worked well for my situation. Haven't tested the second way.

Related

nodejs pass parameter after parameter

I know that the title looks crazy
but here is the problem
i have a function with 2 parameter both have default value
function a(author="unknown" message="my message"){
...
}
i can access it using a("john", "hello")
but what if i need to change only the 2nd parameter?
a("","hello")
if i set the first param to "" it change the default
so i need to do?
i want is a(default,message="new message")
An alternative way to achieve this is if you can modify the function to accept an object then you can choose which parameters to pass in.
function a({ author="unknown", message="my message" } = {}){
...
}
then to call it
a({ message: "new message" }
Put simply, no, there is no way in js to get parameters by name, only by order.
You can always consult the docs for that information here.
You should call your function in the below manner.
eg.
a(undefined, "new message")
This will take default value of author and take message parameter.
This is answered properly in one of the question. Check it here

Emulator database onUpdate update.after.ref incorrect

I use the emulator for functions and realtime database, and have a test onUpdate function attached to a secondary database (not the default one).
const admin = require("firebase-admin");
admin.initializeApp();
const ref = admin.app().database("https://non-default-database.firebaseio.com").ref();
exports.test = functions
.region("europe-west1")
.database.instance("non-default-database")
.ref("test/test")
.onUpdate(async (update) => {
let before = update.before.val();
let after = update.after.val();
console.log(`Beforeval: ${before} .:. Afterval: ${after}`);
console.log(update.after.ref.toString());
if (after !== before) { //avoid infinite loop
console.log(
"Before set: ",
(await update.after.ref.once("value")).val(),
(await ref.child(`test/test`).once("value")).val()
);
await update.after.ref.set(false);
console.log(
"After set: ",
(await update.after.ref.once("value")).val(),
(await ref.child(`test/test`).once("value")).val()
);
}
});
Yes, all the function does is set the value it is attached to to false, and spew some info to the console.
If the value at 'test/test' equals false to begin with and we set it to true, this is the console output:
Beforeval: false .:. Afterval: true
http://localhost:9000/test/test
Before set: false true
After set: false false
In the first line we can see that the values before and after are what we expect.
The second output line seems okay, but it looks like it is missing ?ns=non-default-database (first hint).
The third line should be the value as read from the database before setting. First using the update.after.ref reference and second using the root reference of the secondary database. We would expect both to return true since we just set it to be true. However, the first one is false (second hint).
The fourth and last line gives us the same as the previous one but after setting. Here we do get what we expect again.
When we look at the database, the value is still true.
Now let's change the function to not set it to false but to the string foobar, thus await update.after.ref.set(false); becomes await update.after.ref.set("foobar"); and let's look at the console output when we change the value from false to true again:
Beforeval: false .:. Afterval: true
http://localhost:9000/test/test
Before set: false true
After set: foobar true
The first three lines are the same, but hey, look at the fourth line!
The value when reading from update.after.ref is indeed set to the string foobar, but the value when reading from the root ref is still true (third hint).
When we look in the database webpage of the emulator we see that the value is still true.
Something else did change, there is a new database (that we probably missed before) named 'fake-server' that I never made. That database is completely empty though (fourth hint).
Okay let's look at the values using the REST API.
Going to http://localhost:9000/test/test.json gives us null.
Going to http://localhost:9000/test/test.json?ns=non-default-database gives us true.
Going to http://localhost:9000/test/test.json?ns=fake-server gives us null.
If we change await update.after.ref.set(false); to await ref.child('test/test').set(false); it does all work perfectly.
My conclusion from all of this is that when using a secondary server and emulating triggers on those, the update variable passed in by onUpdate forgets to use that second database, uses some non-existing one, which in some way makes the fake-server database appear but doesn't reference the secondary database.

How do you set the value of 'DocumentShowInSiteMap' property in Kentico?

How do you set the value of DocumentShowInSiteMap property in Kentico?
I've tried:
Using the DocumentHelper and TreeHelper apis but with both, this property doesn't give you access to the setter.
Tried running a SQL Query setting the value of dbo.CMS_Document.DocumentShowInSiteMap to 1 and 0. These queries run fine but when I go to the pages app, there is no change in the 'Show in sitemap' property checkbox, ie. setting the database field to 0 doesn't 'untick' this checkbox.
I'm trying to run a scheduled task which will set this property for documents at a specified location automatically. What is the proper way to do this? Any help appreciated.
Have you tried this?
int TheDocumentToModify = 1221;
var PageItem = DocumentHelper.GetDocument(TheDocumentToModify , new TreeProvider());
foreach(var culturePage in PageItem.CultureVersions)
{
culturePage.SetValue("DocumentShowInSiteMap", true);
// May need to apply Workflow check in / check out, see Kentico API examples based on your need.
culturePage.Update();
}
Within code, there is no simple way. Setter should be available within special class DocumentCultureDataInfo and it should be saved with SetObject. This class contains all of culture DB fields and is manipulated by DocumentCultureDataInfoProvider.
This class is an internal property of TreeNode. However I have not tried doing this arbitrary in code and mentioned classes are part of innner API.
Your second approach should work, but documents and their properties are cached and you will need to refresh cache so that new DB value is actually picked up Loading of this property goes through simple GetData in LoadDefaultValues for each TreeNode.
Trevor J Fayas's answer would probably work. I figured this out yesterday and just leaving my solution here just in case:
TreeHelper
.GetDocuments(task.CurrentSiteName, aliaspath, null, false, "", "", "", -1, false, -1)
.Where(doc => doc.DocumentShowInSiteMap)
.ToList()
.ForEach(node =>
{
node.SetValue("DocumentShowInSiteMap", false);
node.Update();
});
Obviously replace aliaspath with the one you need or use DocumentHelper.GetDocuments which takes different parameters.

Nodejs node-sqlite3 run callback not working

I am trying to perform a delete of a row in sqlite db using nodejs and node-sqlite3 package.
When I run the delete command, and manually check the entries, I can see that the query successfully deleted that row but I cant seem to write the code that confirms this.
This is the query
db.run("DELETE FROM Table1 WHERE id=? AND username=?", [id, user], function(error) {
console.log(error);
});
Regardless of a wrong or right input, it outputs null to the console. If the right details are given, it deletes it and prints null, if wrong id and user are given, it still prints null.
Any ideas on what might be wrong?
Thanks
To my prevoius question, the problem was that I've used fat arrow for callback declaration. From javascript documentation I've discovered that in arrow function (fat arrow ), this has lexical scope and so this result undefined and not valued as in library documentation said. Using otherwise anonimous function, this is bounded in dynamic scope and so this.changes is valued.
Now, with code as below, is ok:
var sql = 'update recipes set stars = stars + 1 where id = ?';
db.run(sql,
[
1 // id = 1 execute update - if id = 11111 do nothing
], function(err) {
if(err)
throw err;
console.log("VALUE CHANGES: " + this.changes + " - " + util.inspect(this, { showHidden: false, depth: null }));
if(this.changes == 1)
console.log("WORK DONE");
else
console.log("NOTHING DONE");
});
Here more explanations: https://github.com/mapbox/node-sqlite3/issues/606
There is nothing wrong in the node and node-sqlite3 behaviour here.
Here are two parts to explain first regarding node and other regarding Sqlite.
Node
Your callback is getting called after execution of the statement. So nothing wrong here, since your callback is getting called (as proved by 'null' as output).
Sqlite
Delete query in Sqlite deletes if condition given in where clause evaluates to true, otherwise nothing is deleted.
Referring from node-sqlite3 documentation's Database#run api:
callback (optional): If given, it will be called when an error occurs
during any step of the statement preparation or execution, and after
the query was run. If an error occurred, the first (and only)
parameter will be an error object containing the error message. If
execution was successful, the first parameter is null.
So, in your case query execution succeeds without any error, resulting in error argument to callback function null as you see in output.
Further, if you want to check if any row was actually removed, you can use changes property as mentioned in the documentation:
If execution was successful, it contains two properties named "lastID"
and "changes" which contain the value of the last inserted row ID and
the number of rows affected by this query respectively. Note that
"lastID" only contains valid information when the query was a
successfully completed INSERT statement and "changes" only contains
valid information when the query was a successfully completed UPDATE
or DELETE statement. In all other cases, the content of these
properties is inaccurate and should not be used. The .run() function
is the only query method that sets these two values; all other query
methods such as .all() or .get() don't retrieve these values.
Hope it helps...
I had similar problem, callbacks just would not fire. Period. The problem was that elsewhere I was calling process.exit(1), so my code was exiting before the the callbacks had a chance to return.
Search for process.exit, that may (or may not) save you hours of debugging and blaming sqlite :)
Off the topic: What bugs my mind is why they all-cap ID in lastID. It's not like it's an abbreviation like SQL or USA. It stands for Identification, which is one word.

ActionDispatch::ClosedError when testing Rails 3.1 model creation (RSpec/Cucumber)

I am creating a web application with Ruby on Rails 3.1 (RC1). I am using Factory Girl, RSpec and Cucumber (with Capybara) for testing, but I am experiencing unexpected raised ActionDispatch::ClosedErrors some of the times (not every time) when I am creating new users (through the User model's create action). Below is the error message that I get:
Cannot modify cookies because it was closed. This means it was already streamed
back to the client or converted to HTTP headers. (ActionDispatch::ClosedError)
The error is raised when using these ways of creating users:
Creation using Factory Girl
Factory.create( :user )
Factory.build( :user ).save
Basic creation
User.create( { ... } )
User.new( { ... } ).save
What is funny is that they do work during some test, but not in others, and it does not seem random, although I cannot figure out the reason. Below is an excerpt from my code:
users_controller_spec.rb
require 'spec_helper'
def user
#user ||= Factory.create( :user )
end
def valid_attributes
Factory.attributes_for :user
end
describe UsersController do
describe 'GET index' do
it 'assigns all users as #users' do
users = [ user ] # The call to user() raises the error here
get :index
assigns[ :users ].should == users
end
end
describe 'GET show' do
it 'assigns the requested user as #user' do
get :show, id: user.id # The call to user() raises the error here
assigns[ :user ].should == user
end
end
However, the error is not raised in the following code block:
describe 'GET edit' do
it 'assigns the requested user as #user' do
get :edit, id: user.id # This raises no error
assigns[ :user ].should == user
end
end
Any other method below this does not raise the error, even though I am creating users in the exact same way.
Any suggestions to what I might be doing wrong would be greatly appreciated!
Someone posted a workaround here
https://github.com/binarylogic/authlogic/issues/262#issuecomment-1804988
This is due to the way rails 3 streams the response now. They posted a fix in edge for the same issue in flash but not in cookies yet. For now I have turned off my request specs. I am going to look at the problem this weekend if no one gets to it before then.
https://github.com/rails/rails/issues/1452
Just so we don't have to follow links, here's my modified version of the authlogic workaround:
class User < ActiveRecord::Base
acts_as_authentic do |c|
c.maintain_sessions = false if Rails.env == "test"
end
end
Rather than deal with ensuring session management on every .save call, I just turn them off if I'm testing.

Resources