Skip to content

Replace nulls in props from Rails with undefined #1293

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Aug 15, 2023

Conversation

ahangarha
Copy link
Collaborator

Update/Rebase to master for #1273 PR.

@ahangarha ahangarha changed the title Feature/add option to replace null updated Replace nulls in props from Rails with undefined - updated Jul 27, 2023
@ahangarha ahangarha requested a review from justin808 July 27, 2023 19:31
@ahangarha ahangarha added this to the 3.0 milestone Jul 27, 2023
Copy link
Collaborator

@Judahmeek Judahmeek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ahangarha we need a test for this functionality

@ahangarha ahangarha self-assigned this Jul 28, 2023
@ahangarha ahangarha modified the milestones: 3.0, 3.1 Aug 1, 2023
@MaySoMusician
Copy link
Contributor

I tried to set up the test environment with appraisal but couldn't get it. At least we have to upgrade appraisal to the latest because the currently-specified version of it doesn't correctly install the dependencies by bundle exec appraisal install

@ahangarha
Copy link
Collaborator Author

the currently-specified version of it doesn't correctly install the dependencies by bundle exec appraisal install

@MaySoMusician May you be more specific? Have you tried to work from this branch?

@ahangarha
Copy link
Collaborator Author

With changes in e77835e, the logic is implemented in Ruby side. The implementation may seem tricky and risky, but I think because of the nature of json, I could manage to match only key":null cases and ensure no other null strings would get replaced.

I would like to see if any input can break this logic. I included a highly possible similar match in the tests to ensure it doesn't get replaced.

@MaySoMusician May you please check if this solution offers the exact result you are looking for?

@ahangarha ahangarha changed the title Replace nulls in props from Rails with undefined - updated WIP - Replace nulls in props from Rails with undefined - updated Aug 7, 2023
@ahangarha ahangarha marked this pull request as draft August 7, 2023 18:51

# This regex matches key:value with null values while ensuing no string with similar
# pattern gets matched. It doesn't include null values in arrays.
props.to_json.gsub(/([^\\]":)null([,}\]])/, '\1undefined\2')
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regexps are great for many units tests to demonstrate the regexp is correct.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have made some tests to ensure the regex match "key":null and only them (not the one similar to them. I think I have tested the most similar situation but I would like to get examples that break the regex.

@@ -108,7 +118,8 @@ var ReactRailsUJS = {
var className = node.getAttribute(ujs.CLASS_NAME_ATTR);
var constructor = ujs.getConstructor(className);
var propsJson = node.getAttribute(ujs.PROPS_ATTR);
var props = propsJson && JSON.parse(propsJson);
var props = propsJson && (ujs.options.replaceNull ? replaceNullWithUndefined(JSON.parse(propsJson))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not a separate var for the parse result to avoid duplicating that bit?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the ruby solution is good, all js implementations will be removed.

@ahangarha ahangarha force-pushed the feature/add-option-to-replace-null-updated branch from e77835e to 2ba7e74 Compare August 13, 2023 17:12
@ahangarha ahangarha force-pushed the feature/add-option-to-replace-null-updated branch from 2ba7e74 to 3f8f482 Compare August 14, 2023 15:14
@ahangarha
Copy link
Collaborator Author

The commits for updating lock files are extracted into a PR #1297 to keep the PR focused

@Judahmeek Judahmeek changed the title WIP - Replace nulls in props from Rails with undefined - updated Replace nulls in props from Rails with undefined Aug 15, 2023
@Judahmeek Judahmeek marked this pull request as ready for review August 15, 2023 22:42
@justin808
Copy link
Collaborator

Let's rebase on master.

@justin808 justin808 merged commit 00b1254 into master Aug 15, 2023
@justin808 justin808 deleted the feature/add-option-to-replace-null-updated branch August 15, 2023 22:47
data[:react_props] = (props.is_a?(String) ? props : props.to_json)
data[:react_props] = props_to_json(
props,
null_to_undefined: Dummy::Application.config.react.null_to_undefined_props
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@justin808 should we use Dummy::Application here? Dummy is just for tests. I suspect it should be Rails.configuration / Rails.application.config (both are the same), no?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Agree.

@ahangarha can you get a PR in for this ASAP?

Or @lcmen if you can get us a PR, I'll merge ASAP.

Extra credit if you provide a test that shows this bug.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the PR was not ready for merge. To me it was still a WIP with good progress.

I am on it

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey folks,

Just wanted to chime in that this exact issue is breaking the 3.1.0 gem for me on a fresh Rails app.

Following the installation steps outlined in the README produces:

> rails s
=> Booting Puma
=> Rails 7.0.7 application starting in development
=> Run `bin/rails server --help` for more startup options
Puma starting in single mode...
* Puma version: 5.6.7 (ruby 3.1.2-p20) ("Birdie's Version")
*  Min threads: 5
*  Max threads: 5
*  Environment: development
*          PID: 19068
* Listening on http://127.0.0.1:3000
* Listening on http://[::1]:3000
Use Ctrl-C to stop
Started GET "/" for 127.0.0.1 at 2023-08-20 20:15:25 -0400
Processing by HomeController#index as HTML
  Rendering layout layouts/application.html.erb
  Rendering home/index.html.erb within layouts/application
  Rendered home/index.html.erb within layouts/application (Duration: 2.8ms | Allocations: 4135)
  Rendered layout layouts/application.html.erb (Duration: 2.9ms | Allocations: 4247)
Completed 500 Internal Server Error in 8ms (ActiveRecord: 0.0ms | Allocations: 8205)



ActionView::Template::Error (uninitialized constant React::Rails::ComponentMount::Dummy

              null_to_undefined: Dummy::Application.config.react.null_to_undefined_props
                                 ^^^^^):
    2: <p>Find me in app/views/home/index.html.erb</p>
    3:
    4: <!-- erb: paste this in view -->
    5: <%= react_component("HelloWorld", { greeting: "Hello from react-rails." }) %>

app/views/home/index.html.erb:5
^C- Gracefully stopping, waiting for requests to finish
=== puma shutdown: 2023-08-20 20:21:16 -0400 ===
- Goodbye!
Exiting

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please upgrade to 3.1.1.

@lcmen
Copy link

lcmen commented Aug 16, 2023

Hey! I believe this PR makes our controller specs to crash with a following error:

ActionView::Template::Error:
       #   uninitialized constant React::Rails::ComponentMount::Dummy

It comes from Dummy::Application reference in lib/react/rails/component_mount.rb:61 line.

@ahangarha
Copy link
Collaborator Author

@lcmen
Thanks for reporting the issue.

@MaySoMusician
Copy link
Contributor

Hmm... I don't think Ruby can handle this problem due to the JSON spec. A JSON can't contain undefined value so that, in the current implementation, react_ujs will fail to mountComponents where it parse the JSON string from Rails.

My very first post in the discussion has already pointed this out.

react-rails passes the values from Rails to React via data-react-props HTML attribute with JSON, in the specification of which undefined is not defined (no pun intended). Due to this, in the part of converting them to JSON, we have no choice than converting Ruby's nils to JSON's nulls.

Or if you missed my post, you can recognize this limitation by testing the library in a real application (or the test suite, if available...)

@lcmen
Copy link

lcmen commented Aug 16, 2023

The more I think about this, the more I agree with @justin808 response that this should not be a responsibility of the library. What I would suggest as an alternative, is a some kind of serializer callback function that could be customized via config option. It would simply take props and run custom function to serialize props. Something like this (pseudo code):

def props_to_json(props, options = { null_to_undefined: false })
  return props if props.is_a?(String)
  
  return Rails.configuration.react_props_serializer.call(props) if Rails.configuration.react_props_serializer.present?

  # rest of the code
end

This way library gives an escape hatch for projects that require more serialization flexibility but does without imposing any additional constraints.

ahangarha added a commit that referenced this pull request Aug 16, 2023
…eplace-null-updated"

This reverts commit 00b1254, reversing
changes made to 95908b8.
Judahmeek pushed a commit that referenced this pull request Aug 16, 2023
…eplace-null-updated" (#1300)

This reverts commit 00b1254, reversing
changes made to 95908b8.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants