Sinatra not parsing array from params - sinatra

I have a hidden form like this:
= form_for movie, 'create', method: 'POST' do |f|
- Movie.attribute_names.each do |attr|
= f.hidden_field attr.to_sym, value: movie.send(attr)
= f.submit 'Save this Movie', class: 'button'
Some of this attributes are serialized arrays, but padrino seems not to be parsing arrays from strings, so the db raises serialization error when I try to save a record from this attr hash stored in params:
{"title"=>"Iron Man 2",
"directors"=>"[\"Jon Favreau\"]",
"cast_members"=>"[\"Robert Downey Jr.\", \"Gwyneth Paltrow\", \"Don Cheadle\", \"Scarlett Johansson\", \"Sam Rockwell\"]",
"genres"=>"[\"Action\", \"Adventure\", \"Sci-Fi\"]",
"length"=>"124",
"trailer_url"=>"http://imdb.com/video/screenplay/vi1172179225/",
"year"=>"2010",
"languages"=>"[\"English\", \"French\", \"Russian\"]"}
I can parse the array string with this ugly chunk of code:
Movie.serialized_attributes.keys.each do |k|
movie[k] = movie[k].gsub(/\"|\[|\]/, '').split(', ') if movie[k]
end
but that seems just stupid, sinatra should be parsing it automatically (or not?), or maybe I'm generating the form the wrong way. How can I fix this behavior?
Note: just tried generating the form like this:
- Movie.attribute_names.each do |attr|
- if Movie.serialized_attributes.keys.include? attr
= f.hidden_field attr.to_sym, value: movie.send(attr), multiple: true
- else
= f.hidden_field attr.to_sym, value: movie.send(attr)

My advice would be to steer well clear of sending data in the form of arrays and hashes. The data you're trying to send could just as easily be sent as one string and then split on the other side. For instance, why not send the cast members as a comma delimited string and then param["cast_members"].split(/\s*,\s*/) on receipt? Some kind of autosuggest would be prudent or you'll end up with lots of misspelt names. This will help you delimit the data further by replacing entries with slugs before the data is sent. Look at the way StackOverflow does the tagging field.
If you really need to send as an array, consider converting it to JSON and then sending it and deserializing on the other end instead of doing it yourself.

I wanted to maintain LOC and readability at best possible level, so worked around it with AJAX:
= link_to 'Save this movie', class: 'button', id: "create_movie_#{movie_id}"
- content_for :scripts do
:javascript
$(function(){
$('#create_movie_#{movie_id}').on('click', function(){
$.post("/movies/create.json",
{movie: #{movie.to_json}},
function(r){
console.log('Response arrived:')
console.log(r)
})
})
})
# controller.rb
post :create, provides: [:html, :json] do
case content_type
when :json
Movie.create(params[:movie]).to_json
when :html
if #movie = Movie.create(params[:movie])
render 'movies/show'
else
redirect 'movies/fetch', error: 'Something went wrong'
end
end
end

Related

Validating optional fields in react using Zod

Using react-hook-form, and zod within a Next.js project. Trying to get .optional() to work. It works with simple strings. See the schema below.
//works
const FormSchema = z.object({
website: z.string().optional()
});
But it does not work when I add the .url() flag. It only checks for the valid url. If the field is blank, it throws an error. In other words, the field is no longer optional. It accepts a valid url, but not a blank input field. Of course I want it to accept a blank input and a valid url as the only valid inputs.
//not working. the field is no longer optional.
const FormSchema = z.object({
website: z.string().url().optional()
})
Perhaps the problem has to do with the input field returning the wrong data-type when it's blank?
<label className="block">
<span className="block text-white">Website</span>
<input
id="email-input"
type="text"
className={`block border text-lg px-4 py-3 mt-2 border-gray-200 focus:bg-white text-gray-900 focus:ring-purpleLight focus:ring-2 outline-none w-full disabled:bg-gray-100 disabled:text-gray-400 disabled:cursor-not-allowed`}
{...register("website")}
disabled={isSubmitting}
placeholder="Website"
value={undefined}
/>
</label>
{errors.website && (
<p className="mt-1 text-sm text-red-600">
{errors.website.message}
</p>
)}
Perhaps the problem has to do with the input field returning the wrong data-type when it's blank?
I think this is likely your problem, although I'm not familiar with what react-hook-form might be doing to transform the input before handing it to your schema. The empty field will have the empty string '' as a value when it's blank. If you just use zod directly:
z.string().optional().parse(''); // succeeds because '' is a string
// Whereas this will fail because although '' is a string, it does not
// match the url pattern.
z.string().url().optional().parse('');
.optional() allows for the input value to be undefined so for example:
// These will both successfully parse because of `optional`
z.string().optional().parse(undefined);
z.string().url().optional().parse(undefined);
Going out slightly on a limb here, but if you wanted '' to pass you could add a preprocess step where you convert '' to undefined:
const s = z.preprocess(
(arg) => (arg === "" ? undefined : arg),
z.string().url().optional()
);
console.log(s.safeParse("")); // success (data: undefined)
console.log(s.safeParse("test")); // failure (not a url)
console.log(s.safeParse(undefined)); // success (data: undefined)

approach for validated form controls in AngularJS

My teammates and I are learning AngularJS, and are currently trying to do some simple form field validation. We realize there are many ways to do this, and we have tried
putting input through validation filters
using a combination of controller and validating service/factory
a validation directive on the input element
a directive comprising the label, input and error output elements
To me, the directive approach seems the most "correct". With #3, we ran into the issue of having to communicate the validation result to the error element (a span sibling). It's simple enough to do some scope juggling, but it seemed "more correct" to put the span in the directive, too, and bundle the whole form control. We ran into a couple of issue, and I would like the StackOverflow community's input on our solution and/or to clarify any misunderstandings.
var PATTERN_NAME = /^[- A-Za-z]{1,30}$/;
module.directive("inputName", [
function () {
return {
restrict: "E",
require: "ngModel",
scope: {
fieldName: "#",
modelName: "=",
labelName: "#",
focus: "#"
},
template: '<div>' +
'<label for="{{fieldName}}">{{labelName}}</label>' +
'<input type="text" ng-model="modelName" id="{{fieldName}}" name="{{fieldName}}" placeholder="{{labelName}}" x-blur="validateName()" ng-change="validateName()" required>' +
'<span class="inputError" ng-show="errorCode">{{ errorCode | errorMsgFltr }}</span>' +
'</div>',
link: function (scope, elem, attrs, ngModel)
{
var errorCode = "";
if (scope.focus == 'yes') {
// set focus
}
scope.validateName = function () {
if (scope.modelName == undefined || scope.modelName == "" || scope.modelName == null) {
scope.errorCode = 10000;
ngModel.$setValidity("name", false);
} else if (! PATTERN_NAME.test(scope.modelName)) {
scope.errorCode = 10001;
ngModel.$setValidity("name", false);
} else {
scope.errorCode = "";
ngModel.$setValidity("name", true);
}
};
}
};
}
]);
used as
<form novalidate name="addUser">
<x-input-name
label-name="First Name"
field-name="firstName"
ng-model="firstName"
focus="yes"
model-name="user.firstName">
</x-input-name>
<x-input-name
label-name="Last Name"
field-name="lastName"
ng-model="lastName"
model-name="user.lastName">
</x-input-name>
...
</form>
First, because both form and input are overridden by AngularJS directives, we needed access to the ngModel API (ngModelController) to allow the now-nested input to be able to communicate validity to the parent FormController. Thus, we had to require: "ngModel", which becomes the ngModel option to the link function.
Secondly, even though fieldName and ngModel are given the same value, we had to use them separately. The one-way-bound (1WB) fieldName is used as an attribute value. We found that we couldn't use the curly braces in an ngModel directive. Further, we couldn't use a 1WB input with ngModel and we couldn't use a two-way-bound (2WB) input with values that should be static. If we use a single, 2WB input, the model works, but attributes like id and name become the values given to the form control.
Finally, because we are sometimes reusing the directive in the same form (e.g., first name and last name), we had to make attributes like focus parameters to be passed in.
Personally, I would also like to see the onblur and onchange events bound using JavaScript in the link function, but I'm not sure how to access the template markup from within link, especially outside/ignorant of the larger DOM.

ZEND: Return HTML as string using Render?

I have:
$this->_helper->layout->disableLayout();
$this->_helper->viewRenderer->setNoRender();
$this->render("voucher-list");
The problem is, when I execute:
$this->render("voucher-list");
It actually prints the information to screen. I want to return the data as an HTML string. This DOES NOT work:
$htmlcontent = $this->render("voucher-list");
How would I go about returning this information as a string?
If you want to send the data into JSON format then you need to make the array of data and use the following code to send the data:
$data = array(3,4,'test', 'my-name' => 3,4); //suppose this is your data
$this->_helper->viewRenderer->setNoRender(true);
$this->view->layout()->disableLayout();
echo Zend_Json::encode($data);
And you will get the data in JSON format.
See this: Zend Framework JSON Output

'if' in ext.xtemplate for inexistent JSON object

My JSON has most of the time these two in its structure: "type" and "comments". Sometimes it has instead "type", "survey", "comments". So, I'd like to use "if" to let ext.xtemplate showing the ones it finds. For instance I've tried this but doesn't work:
new Ext.XTemplate(
'<div style="text-align:justify;text-justify:inner-word">',
'<b>Type:</b> {type}<br/>',
'<tpl if="survey">',
<b>Survey:</b> {survey}<br/>',
'</tpl>',
'<b>Comments:</b> {comments}',
'</div>'
I've tried instead these ones too but with no success:
<tpl if="survey != {}">
<tpl if="survey != undefined">
how could be the right way to detect an inexistent object?, thanks in advance.
PS. I'm using ExtJS 3.4
Use values local variable, for example:
var tpl = new Ext.XTemplate(
'<div style="text-align:justify;text-justify:inner-word">',
'<b>Type:</b> {type}<br/>',
'<tpl if="values.survey">',
'<b>Survey:</b> {values.survey}<br/>',
'</tpl>',
'<b>Comments:</b> {values.comments}',
'</div>'
);
Besides values there are also other variables available, which are helpful in some cases: parent, xindex, xcount.
Template after preprocesing is executed as a function, your template look like this:
function (values, parent, xindex, xcount){ // here are values, parent, etc
with(values){ // each property of values will be visible as local variable
return [
'<div style="text-align:justify;text-justify:inner-word"><b>Type:</b> ',
(values['type'] === undefined ? '' : values['type']),
'<br/>',
this.applySubTemplate(0, values, parent, xindex, xcount), // each <tpl> is converted into subtemplate
'<b>Comments:</b> ',
(values.comments === undefined ? '' : values.comments),
''
].join('');
}
}
This knowledge usually helps with understanding XTemplates.
Example usage of mentioned variables: http://jsfiddle.net/gSHhA/
I use <tpl if="!!survey>"

Perl & MongoDB binary data

From the MongoDB manual:
By default, all database strings are UTF8. To save images, binaries,
and other non-UTF8 data, you can pass the string as a reference to the
database.
I'm fetching pages and want store the content for later processing.
I can not rely on meta-charset, because many pages has utf8 content but wrongly declaring iso-8859-1 or similar
so can't use Encode (don't know the originating charset)
therefore, I want store the content simply as flow of bytes (binary data) for later processing
Fragment of my code:
sub save {
my ($self, $ok, $url, $fetchtime, $request ) = #_;
my $rawhead = $request->headers_as_string;
my $rawbody = $request->content;
$self->db->content->insert(
{ "url" => $url, "rhead" => \$rawhead, "rbody" => \$rawbody } ) #using references here
if $ok;
$self->db->links->update(
{ "url" => $url },
{
'$set' => {
'status' => $request->code,
'valid' => $ok,
'last_checked' => time(),
'fetchtime' => $fetchtime,
}
}
);
}
But get error:
Wide character in subroutine entry at
/opt/local/lib/perl5/site_perl/5.14.2/darwin-multi-2level/MongoDB/Collection.pm
line 296.
This is the only place where I storing data.
The question: The only way store binary data in MondoDB is encode them e.g. with base64?
It looks like another sad story about _utf8_ flag...
I may be wrong, but it seems that headers_as_string and content methods of HTTP::Message return their strings as a sequence of characters. But MongoDB driver expects the strings explicitly passed to it as 'binaries' to be a sequence of octets - hence the warning drama.
A rather ugly fix is to take down the utf8 flag on $rawhead and $rawbody in your code (I wonder shouldn't it be really done by MongoDB driver itself?), by something like this...
_utf8_off $rawhead;
_utf8_off $rawbody; # ugh
The alternative is to use encode('utf8', $rawhead) - but then you should use decode when extracting values from DB, and I doubt it's not uglier.
Your data is characters, not octets. Your assumption seems to be that you are just passing things through as octets, but you must have violated that assumption earlier somehow by decoding incoming text data, perhaps even without you noticing.
So simply do not decode, data stay octets, storing into the db won't fail.