Skip to content

Conversation

aaemnnosttv
Copy link
Contributor

@aaemnnosttv aaemnnosttv commented Jul 5, 2017

Hey guys,

I thought I would post this here for some feedback before I go on and do much more.

I've started with the meta commands as it seemed like the easiest way to affect the most commands.

I added some things like PHPUnit which helped a lot with testing the class I added; that OK?

I expect some things will need to change but let me know if we're on the right track here, or if you'd like me to be doing something different while things are more flexible.

Resolves wp-cli/ideas#42

Copy link
Member

@danielbachhuber danielbachhuber left a comment

Choose a reason for hiding this comment

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

Great start! Left a few comments inline

"""
And I run `wp post meta set 1 meta-key --format=json < input.json`
When I run `wp post meta pluck 1 meta-key foo.bar.baz`
Copy link
Member

Choose a reason for hiding this comment

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

What if this were space limited? e.g.

wp post meta pluck 1 meta-key foo bar baz

I think space delimited is closer to existing WP-CLI patterns, unless there's a specific reason you're using periods.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I thought about that. My initial thought was to use a single string for it and make the delimiter configurable which is how it is now. A space could still be used as it is but it would require wrapping in quotes. The command is a little lengthy with arguments as it is, I think the dot-notation it's a little easier to read.

Copy link
Member

@schlessera schlessera Jul 7, 2017

Choose a reason for hiding this comment

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

I think the preferred approach should be to accept an arbitrary number of positional arguments with each one representing one level.

The benefit is that you can have both periods and spaces be part of the keys:

# foo -> bar -> baz
wp post meta pluck 1 meta-key foo bar baz

# foo.bar -> baz
wp post meta pluck 1 meta-key foo.bar baz

# foo bar -> baz
wp post meta pluck 1 meta-key "foo bar" baz

Any mechanism which needs you to provide all levels in one positional argument will force you to have one arbitrary character be impossible to use as a key identifier.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That sounds like a good idea. For patch then, should we use an associative argument for the new value or continue to use the last positional argument as the new value?

Copy link
Member

Choose a reason for hiding this comment

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

For patch then, should we use an associative argument for the new value or continue to use the last positional argument as the new value?

Last positional argument seems reasonable to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will the Synopsis parser allow a <parts...> [<value>] ?

"""
When I try `wp post meta pluck 1 meta-key foo`
Then STDOUT should be empty
Copy link
Member

Choose a reason for hiding this comment

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

We should set an explicit check for return code here too:

And the return code should be 1

* default: .
* ---
*
* [--format=<format>]
Copy link
Member

Choose a reason for hiding this comment

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

Are these formats correct?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is mostly copied from the existing get subcommand. The table output is a misnomer for sure as it does not output as a table.

It runs through \WP_CLI::print_value so it should probably be updated with the options it supports.

* default: .
* ---
*
* [--mode=<mode>]
Copy link
Member

Choose a reason for hiding this comment

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

If a user supplies a new value but uses --mode=unset, what's the behavior?

Would it make more sense to have a separate method for deleting?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No test for this yet but it would unset the key which is different than say wp post meta patch 1 meta-key foo.bar '' and just setting it to an empty value.

I like the idea of using a separate command but can't think of a good name to use for that. I think that it can work under the umbrella of patch as it is still a nested value modification unless we can come up with a verb that makes more sense? It's "patching" the value with the key removed.

I was also thinking that there may be a merge mode a merge of data rather than just replacing, but I pulled back on it as it probably wouldn't be very practical.

bin/test.sh Outdated

set -ex

phpunit
Copy link
Member

Choose a reason for hiding this comment

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

We should make this a part of the default package scaffold too: wp-cli/scaffold-package-command#121

@aaemnnosttv
Copy link
Contributor Author

Commands have been updated to use positional args as the target key path, and the concept of a delimiter has been removed. I also updated the assertions to use JSON for consistency with the input.

We still need to come to a decision as to how unset should be used either as a patch flag or separate command. I found a similar case which we will need to make a similar decision for in the case of wanting to set a value for a non-existent key.

By default, the command errors out if the key does not exist. If we want to create the key with the value (ie. insert it) then we need a way to set that in the command.

This is where I am looking for feedback from you guys. I'm not a big fan of using/keeping the mode flag, but I also don't really like the idea of adding 2 more named subcommands which are slightly different than patch, particularly because I can't think of any good names for the operations that wouldn't be confused for other crud commands. unset could be confused with delete, and something like insert could be confused with add. Thoughts?

@danielbachhuber
Copy link
Member

This is where I am looking for feedback from you guys. I'm not a big fan of using/keeping the mode flag, but I also don't really like the idea of adding 2 more named subcommands which are slightly different than patch, particularly because I can't think of any good names for the operations that wouldn't be confused for other crud commands.

This is my sentiment too. I don't have a strong opinion one way or another at this point (--mode=<mode> argument vs. separate subcommands).

Another idea would be: wp option patch (add|remove|update|delete).

@danielbachhuber
Copy link
Member

How does this work with numerically indexed values?

@aaemnnosttv
Copy link
Contributor Author

Another idea would be: wp option patch (add|remove|update|delete).

I like this the most out of all the options so far 👍

How does this work with numerically indexed values?

Good question! The argument will be interpreted as a string I believe and will check for a key with a loose comparison. The traverser can obviously handle any valid type as keys to navigate, the limitation is more on taking the input from the command args where the type will have to be inferred. I don't think there will be a perfect solution here, but I think the only situation where it would be problematic is if a key existed for both the string and numeric value.

E.g.

array(
 1 => 'int one',
 '1' => 'string one',
)

This seems like an edge case that isn't worth handling, but it is something we should look into and make sure that it works reasonably well with numeric keys.

I'll add some tests for this and keep this in mind when building out add/insert.

@danielbachhuber
Copy link
Member

I'll add some tests for this and keep this in mind

Great, thanks. I don't think we need a solution for the ambiguous scenario either, but it would be good to have a test with explicit assertions of behavior.

@aaemnnosttv
Copy link
Contributor Author

Update! Pluck and patch are functionally complete for meta commands 🎉

Patch now requires the action to be set as the first argument (insert|update|delete) which works quite nicely!

More tests have been added for everything, including the behavior with indexed keys.

I thought indexed keys would be more of a pain than they were, but it turns out we only needed to cast them to integers (when they look like one) and everything just works. I tried to create a scenario for the edge case where I was expecting this to fail using a string integer for the key, but found this quite hard to do. Both JSON and PHP convert string integers to integers when serializing.

serialize( ["0" => "foo"] )
// string(20) "a:1:{i:0;s:3:"foo";}"

Who knew!? 😁

The question now is whether this looks good enough to start replicating to the rest of the entities?
Also, I was thinking about namespaces. Should \WP_CLI\RecursiveDataStructureTraverser be moved under \WP_CLI\Entity\RecursiveDataStructureTraverser ? I realize that probably can't be done with the other classes in the package since they existed before the split, but it seems like it might be a good idea going forward.

@danielbachhuber
Copy link
Member

Update! Pluck and patch are functionally complete for meta commands

Sweet :) Looking good on my end. Given the amount of code landing, I'd like for @schlessera to give it a review too.

Should \WP_CLI\RecursiveDataStructureTraverser be moved under \WP_CLI\Entity\RecursiveDataStructureTraverser?

Yes, I think this would be a good idea.

I realize that probably can't be done with the other classes in the package since they existed before the split, but it seems like it might be a good idea going forward.

Correct, and sounds good.

* default: plaintext
* options:
* - plaintext
* - json
Copy link
Member

Choose a reason for hiding this comment

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

Could we get a couple useful ## EXAMPLES here?

Copy link
Member

Choose a reason for hiding this comment

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

@aaemnnosttv Bump here.

* default: plaintext
* options:
* - plaintext
* - json
Copy link
Member

Choose a reason for hiding this comment

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

Could we get a couple useful ## EXAMPLES here?

Copy link
Member

Choose a reason for hiding this comment

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

@aaemnnosttv Bump here

@aaemnnosttv
Copy link
Contributor Author

Hey guys, I just pushed another update with many things addressed. One big improvement is that I found a way to reliably test if STDIN was passed or not which eliminates the possibility of the last argument being mistakenly used for the value if the STDIN is empty.

I added a method on a new Entity\Utils class for this although I'm guessing this could be useful for core as well. I thought we can start with it here and then just replace the import if/when it is assimilated into core?

I still need to add a few extra functional test cases that were requested, but I think we're really close to something that can be replicated to other commands.

@schlessera
Copy link
Member

Yes, let's keep the function within Entity\Utils for now and only refactor when it is effectively needed elsewhere.

aaemnnosttv and others added 2 commits August 1, 2017 22:01
@danielbachhuber
Copy link
Member

@aaemnnosttv Aside from the couple remaining small nits, are you happy enough with this to land it?

@schlessera Can you give this one final review?

/**
* Unset the value at the given key path.
*
* @param $key_path
Copy link
Member

Choose a reason for hiding this comment

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

Type hint missing. string|int|array ?

*/
public function exists( $key ) {
return ( is_array( $this->data ) && array_key_exists( $key, $this->data ) ) || ( is_object( $this->data ) && property_exists( $this->data, $key ) );
return ( is_array( $this->data ) && array_key_exists( $key, $this->data ) ) ||
Copy link
Member

Choose a reason for hiding this comment

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

In terms of readability, pushing the operator to the next line is preferable, i.e.:

if ( $condition1
     || $condition2
     || $condition3 ) {
}

@danielbachhuber
Copy link
Member

@aaemnnosttv Aside from the couple remaining small nits, are you happy enough with this to land it?

Bump @aaemnnosttv

@aaemnnosttv
Copy link
Contributor Author

@danielbachhuber I'm much happier with it now that it does not have the edge case that @schlessera mentioned. I'm guessing there may be a few small abstractions to make which might come up when replicating to the other commands, but I think it's in a good spot. Do you guys feel like it's functionally ready to start rolling it out to other entities?

@schlessera
Copy link
Member

@aaemnnosttv Yes, I think the functionality is good now. Feel free to attack the other commands as well, while abstracting as needed to keep the code DRY.

@danielbachhuber danielbachhuber changed the title Add pluck and patch subcommands [WIP] Add pluck and patch subcommands Aug 11, 2017
@danielbachhuber danielbachhuber added this to the 1.1.0 milestone Aug 11, 2017
@danielbachhuber
Copy link
Member

@aaemnnosttv Let's go ahead and merge this then and handle the others in subsequent PRs.

@danielbachhuber
Copy link
Member

Created a new issue for wp option (patch|pluck) #46

@aaemnnosttv As far as I can think of, wp option * and wp * meta are the only two classes of commands where this would be helpful. Can you think of any others?

@aaemnnosttv
Copy link
Contributor Author

wp site option and wp transient would benefit as well, although Transients would require a PR to the wp-cli/cache-command package. Thoughts?

@danielbachhuber
Copy link
Member

Good call re: wp site option. I don't think wp transient is necessary though... we can wait until someone identifies an actual need for it.

@aaemnnosttv aaemnnosttv deleted the pr/pluck-patch-subcommands branch August 29, 2017 06:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants