I'm just banging my head on the wall because of Rails 4 strong parameters.
Basically, I have a very simple model:
class Band < ActiveRecord::Base
validates_presence_of :name
has_many :users
has_many :donations
attr_accessible :name #I'm using protected_attributes
end
The user must be logged in order to create a band, and when it comes to create one, here it is my controller
def new
#band = Band.new
end
def create
#band = Band.new(band_params)
respond_to do |format|
if #band.save
format.html { redirect_to #band, notice: 'La band รจ stata creata' }
format.json { render action: 'show', status: :created, location: #band }
else
format.html { render action: 'new' }
format.json { render json: #band.errors, status: :unprocessable_entity }
end
end
end
private
def set_band
#band = Band.find(params[:id])
end
def band_params
params.require(:band).permit(:name)
end
With this configuration there's just no way to create a new Band object, not even from the console!!!
b = Band.new
b.save
=> false
b.errors.messages
=> {:name=>["can't be blank"]}
b.band = "band's name"
b.save
=> false
b.errors.messages
=> {}
Band.create!(name: "OneName")
WARNING: Can't mass-assign protected attributes for Band: name
ActiveRecord::RecordInvalid: Validation failed: Name can't be blank
What is happening?
I really have no clue!
Related
Problem summary
I am using webcomponents in elm 0.19.1.
Goal is for the webcomponent to emit an event if the webcomponent attribute "requeststate" changes.
The event is emitted (I can see in the the logging in the webcomponent constructor), but elm is not firing the 'WebcomponentEvent' correctly.
Problem is on both windows 10 and ubuntu 16.04, firefox and chrome.
Didnot test older elm versions.
Steps to reproduce:
I have the problem reproduced in this minimal repo:
https://github.com/Derryrover/elm_webcom_bug
Compile elm with:
elm make src/Main.elm --output elm.js --debug
use a webserver (for example 'serve') to host locally
click the button 'request'. This will change the attribute 'requeststate' on the webcomponent. That in turn triggers the custom event 'created' on the webcomponent. But elm does not receive this event..
Opening the elm-debugger or clicking the 'request' button again will magically make the event fire in elm. Strange.
Also I have made the branch 'tom-experiment'. In this branch I got the event from webcomponent-click working, but only if I directly click on the webcomponent itself.
The importance of this problem
Why do I want to trigger an event on a webcomponent by changing an attribute?
Goal of this approach is also JavaScript interop.
For example I can now use this webcomponent to create a date-time or uuid or do some other javascript magic. This way I can work around ports completely.
A solution for this problem might settle the entire Javascript interop discussion !
Code
This is my Main.elm:
module Main exposing (..)
import Browser
import Html
import Html.Attributes
import Html.Events
import Json.Decode
main =
Browser.sandbox
{ init = init
, view = view
, update = update
}
type alias Model =
{ request : Bool
, received: Bool
}
init : Model
init = { request = False
, received = False
}
type Msg
= Request
| Reset
| WebcomponentEvent
update : Msg -> Model -> Model
update msg model =
case msg of
WebcomponentEvent ->
{ model | received = True}
Request ->
{ model | request = True }
Reset ->
{ model | request = False , received = False}
view : Model -> Html.Html Msg
view model =
Html.div []
[ Html.button [Html.Events.onClick Request] [Html.text "Request"]
, Html.div
[]
[ Html.text "requested:"
, Html.text (if model.request then "true" else "false")
]
, Html.div
[]
[ Html.text "received:"
, Html.text (if model.received then "true" else "false")
]
, Html.button [Html.Events.onClick Reset] [Html.text "Reset"]
, Html.node "webcomponent-test"
[ Html.Events.on "created" (Json.Decode.succeed WebcomponentEvent)
, Html.Attributes.attribute "requeststate" (if model.request == True then "requested" else "idle")
] []
]
This is the webcomponent.js
class Webcomponent extends HTMLElement {
static get observedAttributes() {
return [
"requeststate"
];
}
attributeChangedCallback(name, oldValue, newValue) {
switch (name) {
case "requeststate":
if (newValue === "requested") {
console.log(`requested in webcomponent triggered`);
const customEvent = new CustomEvent('created', {detail: ""});
this.dispatchEvent(customEvent);
}
}
}
constructor() {
super();
this.addEventListener('created', function (e) {
console.log("event triggered as sensed by javascript: ", e.detail, e);
}, false, true);
}
}
customElements.define('webcomponent-test', Webcomponent);
this is my index.html
<!doctype html>
<html>
<head>
<script type="module" src="./webcomponent.js"></script>
<script src="./elm.js"></script>
</head>
<body>
<div id="elm_element"></div>
<script>
var app = Elm.Main.init({
node: document.getElementById('elm_element')
});
</script>
</body>
</html>
This was discussed on the Elm Slack. The issue ended up being a timing issue. The resolution is to change from
this.dispatchEvent(customEvent)
to
requestAnimationFrame(() => this.dispatchEvent(customEvent))
in the custom element, as can be seen in this ellie https://ellie-app.com/cqGkT6xgwqKa1.
Thanks to #antew for the final solution.
There is another potential cause:
This attributeChangedCallback runs before the element is connected to the DOM
attributeChangedCallback(name, oldValue, newValue) {
switch (name) {
case "requeststate":
if (newValue === "requested") {
console.log(`requested in webcomponent triggered`);
const customEvent = new CustomEvent('created', {detail: ""});
this.dispatchEvent(customEvent);
}
}
}
Add
connectedCallback(){
console.log("Now I am ready to emit Events");
}
to verify your dispatchEvent doesn't run too soon.
requestAnimationFrame (or setTimeout) are workarounds to wait till the Event Loop is empty (and thus connectedCallback ran)
(I don't know your use case) You could also test for oldValue === null or this.isConnected in your attributeChangedCallback
Also note you probably need bubbles:true and composed:true on that CustomEvent when shadowDOM is involved.
So, I have the following code, but flow errors keep popping up. I've tried to cast the Object.entries, but just won't work - others things to. Any insight?
type Fields = {
name: string,
func: (*) => boolean
};
type S = {
key1: Fields,
bill: Fields
}
var a: S = {
key1: {name: 'mary', func: (str) => str === 'mary'},
bill: {name: 'bill', func: (str) => str === 'bill'}
}
var c = Object
.entries(a)
.map(([key, obj]) => obj.func(key) ? obj : false)
.filter(f => f)
.reduce((acc, c) => {
return 'something here'
}, {});
I've left some things off, but the slow is the same. Flow is reading that entries as a return Tuple Type. I've tried all sorts of things, but instead of mudding things up, I left it untouched.
I can't seem to annotate the destructured items here ([key, obj]), get tuple errors...
Any assistance on getting that code assigned to var c, to work with annotations etc..?
The errors I get:
Cannot call method on mixed type (from obj.func)
Cannot assign value in Tuple etc..
The error is accurate. Object.entries has the type
entries(object: any): Array<[string, mixed]>;
It has no way to know what the type of the second item in the tuple will be. That means your code
.map(([key, obj]) => obj.func(key) ? obj : false)
would need to do
.map(([key, obj]) => {
if (typeof obj.func !== 'function') throw new Error();
return obj.func(key) ? obj : false;
})
so that flow knows that it is guaranteed to be a function.
Alternatively, you could change your data structure to use a type where the second item in the tuple has a guaranteed type, like Map, e.g.
type Fields = {
name: string,
func: (string) => boolean
};
type S = Map<string, Fields>;
var a: S = new Map([
['key1', {name: 'mary', func: (str) => str === 'mary'}],
['bill', {name: 'bill', func: (str) => str === 'bill'}],
]);
var c = Array.from(a, ([key, obj]) => obj.func(key) ? obj : false)
.filter(f => f)
.reduce((acc, c) => {
return 'something here'
}, {});
In my case, I had:
let objectsByName : { [string] : MyObjectType } = {}; //simple map
...
objectsByName[object.name] = object; //call repeatedly to populate map.
...
let results : any[] = []; //next we will populate this
Trying to operate on it like this failed for Flow (though this is executable JavaScript):
for (let [name : string, object : MyObjectType] of Object.entries(objectsByName))
{
let result = doSomethingWith(object); //<- error on arg
results.push(result);
}
This succeeded for Flow:
for (let name : string in objectsByName)
{
let object = objectsByName[name];
let result = doSomethingWith(object); //<- error on arg
results.push(result);
}
It is annoying having to change code structure to suit a supposedly non-intrusive system like Flow comment types, which I chose in the hopes of making my code completely oblivious to Flow's presence. In this case I have to make an exception and structure my code as Flow wants it.
Replacing Object.entries with Object.keys + lookup fixes flow errors for me assuming the input object is properly typed.
i.e. replace Object.entries(a) with Object.keys(a).map(key => [key, a[key]])
This works with flow:
type Fields = {
name: string,
func: (*) => boolean
};
type S = {
key1: Fields,
bill: Fields
}
var a: S = {
key1: {name: 'mary', func: (str) => str === 'mary'},
bill: {name: 'bill', func: (str) => str === 'bill'}
}
var c = Object
.keys(a)
.map(key => a[key].func(key) ? obj : false)
.filter(f => f)
.reduce((acc, c) => {
return 'something here'
}, {});
I have a project in symfony that I would like to let my users upload an image for their "avatar" field. I have found many posts about how to "extend" the table which I have with the schema below:
Member:
inheritance:
type: column_aggregation
extends: sfGuardUser
columns:
idmember: { type: integer }
birthday: { type: date }
avatar: { type: string(255) }
bio: { type: string(255) }
The columns get added to the table just fine, but when I go to change the widget to a sfWidgetFormInputFileEditable it breaks. Here is the Form.class file:
$file_src = $this->getObject()->getAvatar();
if ($file_src == '')
{
$file_src = 'default_image.png';
}
$this->widgetSchema['avatar'] = new sfWidgetFormInputFileEditable(array(
'label' => ' ',
'file_src' => '/uploads/avatar/'.$file_src,
'is_image' => true,
'edit_mode' => true,
'template' => '<div>%file%<br />%input%</div>',
));
and "save" function of the form:
if($this->isModified())
{
$uploadDir = sfConfig::get('sf_upload_dir');
$thumbnail = new sfThumbnail(150, 150);
$thumbnail2 = new sfThumbnail(800, 800);
if($this->getAvatar())
{
$thumbnail->loadFile($uploadDir.'/avatar/'.$this->getAvatar());
$thumbnail->save($uploadDir.'/avatar/thumbnail/'. $this->getAvatar());
$thumbnail2->loadFile($uploadDir.'/avatar/'.$this->getAvatar());
$thumbnail2->save($uploadDir.'/avatar/big/'. $this->getAvatar());
}
}
When I submit the form, I get this error message:
This form is multipart, which means you need to supply a files array as the bind() method second argument.
In the action where you bind the form you should use something like this:
$form->bind($request->getParamater($form->getName()), $request->getFiles($form->getName()));
So you need to pass the uploaded files as the second parameter to the bind method.
I'm getting strange error when trying to read HTTP request in Netty:
java.lang.NullPointerException
at
org.jboss.netty.handler.codec.http.HttpMessageDecoder.skipControlCharacters(HttpMessageDecoder.java:409)
at org.jboss.netty.handler.codec.http.HttpMessageDecoder.decode(HttpMessageDecoder.java:184)
at org.jboss.netty.handler.codec.http.HttpMessageDecoder.decode(HttpMessageDecoder.java:107)
at org.jboss.netty.handler.codec.replay.ReplayingDecoder.callDecode(ReplayingDecoder.java:470)
at org.jboss.netty.handler.codec.replay.ReplayingDecoder.messageReceived(ReplayingDecoder.java:443)
at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:274)
at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:261)
at org.jboss.netty.channel.socket.nio.NioWorker.read(NioWorker.java:351)
at org.jboss.netty.channel.socket.nio.NioWorker.processSelectedKeys(NioWorker.java:282)
at org.jboss.netty.channel.socket.nio.NioWorker.run(NioWorker.java:202)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
at java.lang.Thread.run(Thread.java:679)
the stack trace doesn't end up in my code, and I have no idea how to debug that?
class RouteHandler(default: Tuple2[String, Int]) extends SimpleChannelUpstreamHandler {
override def handleUpstream(ctx: ChannelHandlerContext, e: ChannelEvent) {
e match {
case evt: UpstreamMessageEvent =>
evt.getMessage match {
case req: HttpRequest => {
val projectHdr = req.getHeader("HDR")
RouteHandler.log.info("Project ID: {}", projectHdr)
val backendServerUri = projectHdr match {
case null => default
case uri: String =>
if (ObjectId.isValid(projectHdr)) {
val serverData = MappingService.resolveServer(new ObjectId(projectHdr))
(serverData.host(), serverData.port())
}
else
default
}
RouteHandler.log.info("Route to {}", backendServerUri)
val pipeline = ctx.getPipeline
pipeline.synchronized {
val handler = new ForwardRequestHandler(backendServerUri._1, backendServerUri._2)
pipeline.get(HANDLER_NAME) match {
case null => pipeline.addLast(HANDLER_NAME, handler)
case _ => pipeline.replace(HANDLER_NAME, HANDLER_NAME, handler)
}
}
}
case z => RouteHandler.log.warn("Can not handle {}", z.getClass)
}
case z: DefaultExceptionEvent => RouteHandler.log.error("Exception from Netty", z.getCause)
case z =>
}
super.handleUpstream(ctx, e)
}
override def exceptionCaught(ctx: ChannelHandlerContext, e: ExceptionEvent) {
RouteHandler.log.error("Caught", e.getCause)
e.getChannel.close()
}
}
So just for the record... The error was that the HttpMessageDecoder was shared across Channels which is not allowed as its not annotated witht #Sharable
I have a partial view where I render, if the user has choosen an option, a button that permit the user to generate automatically a value for a certain field. Please give a look at this picture to understand what I mean:
This is achieved using the following markup on the partial view
<%= Html.LabelFor( model => model.IssueCode )%>
<br />
<% if ( Model.HasCodeGenerator ) { %>
<%= Html.TextBoxFor( model => model.IssueCode, new { style = "width:120px;background-color:#eeeeee;border: solid 2px #dfdfdf", #readonly = "readonly" } )%>
<% if (Model.ModelState == ModelStateEnum.Add) { %>
<button id="codeGenerator" style="font-size: 0.7em;margin-right: 10px">Genera codice fascicolo</button>
<% } %>
<% } else { %>
<%= Html.TextBoxFor(model => model.IssueCode, new { style="width: 120px" })%>
<% } %>
<%= Html.ValidationMessageFor(model => model.IssueCode, "*")%>
As you can see I append always an Html.ValidationMessageFor() at the end of the input field and a ValidationSummary aut the end of the view.
When the user submits the form the first block of code executed by the action is the following
if ( !ModelState.IsValid ) {
//Invalid - redisplay form with errors
return PartialView( "IssueCodeGenerator", model );
}
and this is the result I am getting in all of the three cases
Why the markup code for the button disappear?
Thanks for helping!
1st EDIT:
After validation the IssueCode textbox loose it's readonly="readonly" attribute. This means that the first condition is not meet, I think....
2nd EDIT:
As per the Darin comment I am including
The action that show the Partial View
An extract of the partial that show that the ModelState variable is kept as an hidden control inside the form
The controller Action called by the Partial
The jQuery code that submit the partial
1 - This is the action that shows the partial
[HttpGet]
public ActionResult Create()
{
IssueModel im = new IssueModel()
{
ModelState = ModelStateEnum.Add,
FirmID = _firmManager.GetMyFirmID(),
CreatedDate = DateTime.Now,
LastUpdateDate = DateTime.Now,
HasCodeGenerator = _optionManager.HasIssueCodeGenerator()
};
return PartialView("Issue", im);
}
2 - Extract of the partial Issue.ascx
<% using (Html.BeginForm("SaveOrDelete", "Issue", FormMethod.Post, new { id = "crudForm" })) { %>
<%= Html.HiddenFor(model => model.FirmID) %>
<%= Html.HiddenFor(model => model.IssueID) %>
<%= Html.HiddenFor(model => model.ModelState) %>
3 - This is the controller action called when the form is submitted
[HttpPost]
public ActionResult SaveOrDelete( IssueModel model ) {
if ( !ModelState.IsValid ) {
//Invalid - redisplay form with errors
return PartialView( "Issue", model );
}
try {
Issue i = null;
if ( model.ModelState == ModelStateEnum.Add )
i = new Issue();
else
i = _manager.FindIssueByIssueID( model.IssueID );
if ( model.ModelState != ModelStateEnum.Delete ) {
_manager.BindIssueModel( i, model );
if ( model.ModelState == ModelStateEnum.Add )
i.FirmID = _contactManager.GetMyContact().FirmID;
i.LastUpdateDate = DateTime.Now;
_manager.SaveIssue( i );
} else {
_manager.DeleteIssue( i );
}
return PartialView( "ActionCompleted" );
}
catch ( Exception ex ) {
return PartialView( "ActionError",
new ActionErrorModel() { Message = ex.Message } );
}
}
4 - This is the jQuery code that submit the form
$("#crudForm").submit(function(event) {
event.preventDefault();
$("#crudForm").block();
$.ajax({
type: "post",
dataType: "html",
url: "/Issue/SaveOrDelete",
sync: true,
data: $("#crudForm").serialize(),
success: function(response) {
$("#crudForm").parent().html('').html(response);
$("#crudForm").unblock();
},
error: function(response) {
$("#crudForm").unblock();
}
});
});
Hope that this would help in finding the problem. Thank you.
Because Model.ModelState == ModelStateEnum.Add evals to false?
MVC won't turn off your button html...
Even without seeing the form its pretty clear that Darin is right and your Modelstate enum is getting set to null.