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
Allow alternate adapters to be used for serializing/deserializing #145
Conversation
Thanks for opening the discussion, this is an intersting topic. Here my considerations: first of all I don't like the idea of having ActiveSupport as dependency, just for #constantize. This will force developers to install this gem for a secondary feature, and also could prevent them to decide the Rails version the want to use (in the patch you're automatically excluding all the < 3.2.2 apps). A solution can be borrowing that code and consider it as a Ruby ext. Second, does this JSON serializer guarantees ActiveRecord or Ruby objects to be transparently serialized/unserialized, including their internal state? Could be convenient to implement something similar to MongoId, where there is a more structured representations of the documents? I'm afraid that just invoking #to_json, isn't enough. Third, by implementing this feature, I would like to see a little refactoring. Marshalling shound became an internal adapter (strartegy, if you will) of a more generic Serializer class. The _extend_marshalling should disappear, all the data will pass thru the Serializer, that by default will have a NoOp strategy, or, according to the conf, will change in marshalling/json/yaml/whatever. Fourth, how to specify the adapter in the case of the configuration URI? Maybe redis+json:// ? |
@jodosha - Thanks for the quick feedback, I really appreciate it. After submitting this pull request and thinking about it in bed last night, I had some of the same thoughts about requiring ActiveSupport, as you mentioned. I starting thinking that it would make more sense to take that part out, not worry about constantizing strings, and only allow actual classes/objects that support dump/load to be supplied as values for the There is a concern with this JSON serializer that if you have a Hash where the keys are strings, then you serialize/deserialize, you'll end up with a Hash where the keys are symbols. Strings will not have internal state information such as @html_safe instance variables being saved. Other objects that are extensions of Hash or Array will also lose any internal state they have prior to internal serialization with the current implementation. I''m not sure if there's really a way around this, unless a fake value is added to the serialized Hash/Array that can be interpreted back. I feel like that's kind of a hack though, personally. I was wondering while coding this if the With regards to the configuration URI, I'm not familiar with this at all. Is this something Redis supports by default, or is it exposed by the gem? I haven't worked with Redis in enough detail to know more about it, unfortunately. Thanks! :) |
Alright, I went ahead and made some additional changes to this from what you suggested. It's using the verbiage "strategy" instead of "adapter" now. There are four possible options built in:
I removed the ActiveSupport dependency, and I also added tests for the new strategies. Let me know what you think so far, and also if you could provide more insight about configuration URI's, that would be great so I could maybe take a look at that as well. Lastly, if you decide you want to pull this in after all is said and done, I can squash the commits first if needed. Just let me know! Thanks again :) |
Hmm, I seem to be getting a "Session collision" error quite a bit now, even though I didn't change any of the underlying code. I'll make sure it's nothing I've changed, but if you see anything that might have caused an issue in my changes, please let me know! |
Well, I fixed the errors I had previously, which related to the session collisions. However, I now have an issue with 'json' only being available in Ruby 1.9+, but not Ruby 1.8 or non-MRI Rubies it seems. I suppose I could do the following, though I could use some input from you on how you might want this to go. In the
In the
Thoughts? |
@jodosha - Just wanted to follow up and see if you have any input regarding my above questions. I would love to follow through on this and ideally have it pulled into the redis-store gem if that's an option (after ironing out the questions and json gem issue). |
@mhuggins, the problem with the RUBY_VERSION requirement in the gemspec is that the requirement gets compiled when the gem is pushed, and we'll only push the gem once so it will get compiled with whatever Ruby we're running at build time. Perhaps you could look into how Rails works around this? I'm not sure we need to be too concerned about the loss of internal state resulting from serialization. This should be left as a caveat to those choosing an alternative marshaling strategy. The default state, however, should maintain internal state to preserve backwards compatibility. Regarding URI configuration, perhaps we could pass the strategy (and possibly future options) as "query parameters" in the URI. For example, "redis://localhost/namespace?strategy=json". |
Sorry guys, just back from holidays. |
@jodosha - Given that feedback, I wonder then if a simpler approach could be implemented compared to what I have here. Consider this:
That way, redis-store does not have to be concerned with issues such as the html_safe issue when it comes to serializing JSON, as you pointed out. I can optionally create my own JSON encoder and pass that as the strategy option if I prefer. |
Then again, that would make it difficult to handle the "query parameter" idea in the URI configuration. |
Just FYI, I created a NodeJS module a while ago to unmarshall Ruby serialized data. This could be a quick-fix alternative to the problem presented in this thread: https://gist.github.com/4417808 |
im surprised this wasn't accepted. @jodosha - im wondering why you care so much if JSON doesn't properly de/serialize objects in session. People shouldn't be saving objects into session anyways. The REAL problem is that redis-store only uses ruby Marshal which is a non-standard way of serializing data. You shouldn't care what strategy/adapter people use to load/dump into redis. If a developer creates a crazy adapter and it breaks their app, then it breaks their app. |
when Array | ||
object.each_with_index { |v, i| object[i] = _unmarshal(v) } | ||
when String | ||
object.start_with?(MARSHAL_INDICATORS) ? ::Marshal.load(object) : object |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This won't work. You need to add an asterisk so the MARSHAL_INDICATORS Array is used as multiple arguments for the star_with? method :
object.start_with?(*MARSHAL_INDICATORS)
I suggest adding a test for it. I noticed that there wasn't a test covering marshalling Ruby Marshalled strings from the store.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right, good catch. It could use some more tests, but it doesn't seem like this pull request is going anywhere for me to commit the time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, if it means anything to you, Im using your code on a fairly large website. ;) Thanks for taking the time those months ago to do this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
😄 👍
+1 This is a good feature, JSON is a standard way when the session is not used only in rails/ruby |
👍 |
Hi @mhuggins. There seems to be some interest in getting this PR merged still. If so, can you please update it to make it mergeable with the current master? Anyone else is welcome also to take this PR and do the same and just open a new PR. Thanks! |
Sure thing, I'll try to take a look this weekend. :) |
I'm working on an app that is intended to work with session between Rails and Node.js. The problem is, Node.js can't read a Ruby marshaled object from the session with redis-store's current implementation.
Looking through old issues, I see I'm not the only one with this problem, as issues #77 and #79 were both opened with a similar purpose. The problem is that both of those implementations could not properly unmarshal Ruby objects.
This pull request does two things. First, it creates the concept of an "adapter". An adapter is anything that can respond to
load
anddump
, and it defaults to a new wrapper class that just uses Marshal. (This is to allow for backward compatibility without existing users having to worry about changing their app code.)Second, it introduces a new JSON adapter. This adapter serializes
Hash
,Array
,String
,Numeric
,TrueClass
,FalseClass
, andNilClass
objects into their appropriate JSON representations. Any other non-native JSON class is converted to aString
viaMarshal.dump
. This allows other languages that are sharing the session to parse the JSON session data itself, simply ignoring the native Ruby objects that aren't needed.Example usage:
Here is an example of how easy it is to create a custom adapter:
Of course, we didn't really even need to create an adapter for this very simple scenario. We could use the YAML class as is since it already responds to
load
/dump
:And it's just as easy to integrate into your Rails app:
I would love to hear feedback on the approach. Some new unit tests could also be used (along with updated documentation), but I wanted to ensure you're open to the idea before moving forward with coding anything more into this branch. Thanks!