Instance-variable-like user API

Hi everyone. :slight_smile:

I was wondering if anyone can suggest a better solution to a problem I am trying to solve than one I have come up with. I am trying to create a friendly user-facing API for accessing custom remote variables.

To explain, consider the following simple class and code:

聽聽聽聽---===<<< SNIP >>>===---

class A <SomeBase
聽聽聽def initialize
聽聽聽聽聽@v = 1
聽聽聽end

聽聽聽def inc
聽聽聽聽聽@v = @v + 1
聽聽聽end

聽聽聽def magic
聽聽聽聽聽dosomething(@v)
聽聽聽end

聽聽聽attr_accessor :v
end

a = A.new
a.inc
a.magic

聽聽聽聽---===<<< SNIP >>>===---

Here we have a variable @v that is available for use in each instance of the class A. We modify it in the instance, and outside of it.

I would like something similar in a user-facing API, where the user can create a variable v that they can read from and write to, but the catch is that "v" itself is actually stored remotely in persistent storage. Each read requests the value from the server, and each write sends it. The user will define their own variables (such as "v"), and in presenting the API I won't know these in advance. Don't worry about the storage aspects, networking, serialisation, and cache issues, these are all written and finished. I'm just trying to provide a nice interface to the code I've already written such that the user can almost ignore the fact that they are stored remotely.

I've written my own "magic_accessor" call to add in a "foo" and "foo=" method for each variable "foo" (much like attr_accessor), for the user to use. In this case, we're just using "v", which generates the "v" and "v=" methods when I use it. This changes the above code to the following.

聽聽聽聽---===<<< SNIP >>>===---

class A <SomeBase
聽聽聽def initialize
聽聽聽聽聽self.v = 1
聽聽聽end

聽聽聽def inc
聽聽聽聽聽self.v = v + 1
聽聽聽end

聽聽聽def magic
聽聽聽聽聽dosomething(v)
聽聽聽end

聽聽聽magic_accessor :v
end

a = A.new
a.inc
a.magic

聽聽聽聽---===<<< SNIP >>>===---

What I don't like with the above is that whenever I set v, I have to be careful to use "self.v = <value>", rather than "v = <value>" when I perform assignment, because the latter will just create a local variable and not call the "v=" method I've created. It's fine when called externally on the object, but any internal writes need a "self." prefix. I'm not keen on expecting the user to do this each time, when using "@v" equivalent is so much easier to use.

Is there a better way to implement this, such that the user can write code similarly to the listing at the top? Is there some syntactic sugar I am unaware of that can be used instead of "self.foo"? The goal is to make the user API for this nice and simple- I don't mind if I have to mess about to make it happen, and I've already got a custom "method_missing", and adding code to that is no issue.

Cheers,
Garthy

The only solution for this that I can see is to use some kind of
transaction: you open the transaction, store object state, work with
the object and at the end something inspects instance variables and
writes all changed values. But frankly, OR mappers do exist for Ruby,
ActiveRecord is just one of them. So if it is OK to fill in "RDBMS"
for "remote storage" then you could simply use that. Other than that
I'd stick with using the accessor.

Kind regards

robert

路路路

On Thu, Nov 22, 2012 at 8:17 AM, Garthy D <garthy_lmkltybr@entropicsoftware.com> wrote:

What I don't like with the above is that whenever I set v, I have to be
careful to use "self.v = <value>", rather than "v = <value>" when I perform
assignment, because the latter will just create a local variable and not
call the "v=" method I've created. It's fine when called externally on the
object, but any internal writes need a "self." prefix. I'm not keen on
expecting the user to do this each time, when using "@v" equivalent is so
much easier to use.

Is there a better way to implement this, such that the user can write code
similarly to the listing at the top? Is there some syntactic sugar I am
unaware of that can be used instead of "self.foo"? The goal is to make the
user API for this nice and simple- I don't mind if I have to mess about to
make it happen, and I've already got a custom "method_missing", and adding
code to that is no issue.

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

If you don't mind them accessing all vars through a name, you could have an API like:

class A < SomeBase
def initialize
super
remote[:v] = 1
end

def inc
remote[:v] += 1
end

def magic
do_something(remote[:v])
end
end

Or:

class A < SomeBase
def initialize
super
remote.v = 1
end

def inc
remote.v += 1
end

def magic
do_something(remote.v)
end
end

It makes it a bit more obvious what's happening. The other benefit of this method is that it's not required to declare these with magic_accessor, as whatever #remote returns can deal magically with #/#= or #method_missing.

路路路

On Thursday, 22 November 2012 at 6:55 PM, Robert Klemme wrote:

On Thu, Nov 22, 2012 at 8:17 AM, Garthy D > <garthy_lmkltybr@entropicsoftware.com (mailto:garthy_lmkltybr@entropicsoftware.com)> wrote:

> What I don't like with the above is that whenever I set v, I have to be
> careful to use "self.v = <value>", rather than "v = <value>" when I perform
> assignment, because the latter will just create a local variable and not
> call the "v=" method I've created. It's fine when called externally on the
> object, but any internal writes need a "self." prefix. I'm not keen on
> expecting the user to do this each time, when using "@v" equivalent is so
> much easier to use.
>
> Is there a better way to implement this, such that the user can write code
> similarly to the listing at the top? Is there some syntactic sugar I am
> unaware of that can be used instead of "self.foo"? The goal is to make the
> user API for this nice and simple- I don't mind if I have to mess about to
> make it happen, and I've already got a custom "method_missing", and adding
> code to that is no issue.

The only solution for this that I can see is to use some kind of
transaction: you open the transaction, store object state, work with
the object and at the end something inspects instance variables and
writes all changed values. But frankly, OR mappers do exist for Ruby,
ActiveRecord is just one of them. So if it is OK to fill in "RDBMS"
for "remote storage" then you could simply use that. Other than that
I'd stick with using the accessor.

Kind regards

robert

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

Hi Robert,

What I don't like with the above is that whenever I set v, I have to be
careful to use "self.v =<value>", rather than "v =<value>" when I perform
assignment, because the latter will just create a local variable and not
call the "v=" method I've created. It's fine when called externally on the
object, but any internal writes need a "self." prefix. I'm not keen on
expecting the user to do this each time, when using "@v" equivalent is so
much easier to use.

Is there a better way to implement this, such that the user can write code
similarly to the listing at the top? Is there some syntactic sugar I am
unaware of that can be used instead of "self.foo"? The goal is to make the
user API for this nice and simple- I don't mind if I have to mess about to
make it happen, and I've already got a custom "method_missing", and adding
code to that is no issue.

The only solution for this that I can see is to use some kind of
transaction: you open the transaction, store object state, work with
the object and at the end something inspects instance variables and
writes all changed values. But frankly, OR mappers do exist for Ruby,
ActiveRecord is just one of them. So if it is OK to fill in "RDBMS"
for "remote storage" then you could simply use that. Other than that
I'd stick with using the accessor.

Thanks for that.

I was hoping there might be a way to wrap the accessor-style solution in some way that I hadn't thought of, as this particular solution fits in elegantly with the rest of the code. If it was possible to hook instance variable (eg. @foo) writes and reads in some way I think I'd approach it that way- I just don't know if this is possible.

Having said that, I've been weighing up converting things to use a solution along the lines you mention, most likely through a whitelist or blacklist of variables to save in the current object, which I push across periodically (changes only). This is the less elegant solution from an architectural point-of-view (ie. it's a good solution, just a bit trickier to fit in with my existing code), but I'd bet it would be the easiest to use from a user point-of-view.

In the problem I am trying to solve, ease of use by the user trumps elegance of expression, so I'm thinking the second solution (ie. the one in line with your suggestion) might be the best way to go about it.

Cheers,
Garth

路路路

On 22/11/12 18:25, Robert Klemme wrote:

On Thu, Nov 22, 2012 at 8:17 AM, Garthy D > <garthy_lmkltybr@entropicsoftware.com> wrote:

Hi Arlen,

If you don't mind them accessing all vars through a name, you could have an API like:

class A< SomeBase
def initialize
super
remote[:v] = 1
end

def inc
remote[:v] += 1
end

def magic
do_something(remote[:v])
end

Or:

class A< SomeBase
def initialize
super
remote.v = 1
end

def inc
remote.v += 1
end

def magic
do_something(remote.v)
end

It makes it a bit more obvious what's happening. The other benefit of this method is that it's not required to declare these with magic_accessor, as whatever #remote returns can deal magically with #/#= or #method_missing.

Thanks for putting that together, it's a good solution- and actually very close to what I currently have implemented. :slight_smile: I have a dedicated remote object with "" and "=" to handle arbitrary variables, and as you say, there's no need to declare anything with magic_accessor. It all happens like magic.

I'd also considered your second suggestion as a possible solution too. It's reassuring that people are thinking along the same lines. :slight_smile:

Unfortunately, in use I found the solution a bit awkward. It's perfectly fine for simpler methods, but it gets a bit unwieldy when doing anything complex. At this point I set out to improve it, and ran into the "self." shortcoming. At that point I realised I didn't actually know of a nice way to improve it without some sort of downside, so I thought I'd post to the list to get a second, third, and fourth opinion, in case I'd missed something. :}

Cheers,
Garth

路路路

On 22/11/12 20:25, Arlen Cuss wrote:

I'm not quite getting the remote thing -- is this a ruby-ism, a
variable, a method, what? Doing a google on "ruby remote" doesn't get
me anything that I can make sense of this.

路路路

On Thu, Nov 22, 2012 at 3:55 AM, Arlen Cuss <ar@len.me> wrote:

If you don't mind them accessing all vars through a name, you could have an API like:

class A < SomeBase
def initialize
super
remote[:v] = 1
end

def inc
remote[:v] += 1
end

def magic
do_something(remote[:v])
end
end

Or:

class A < SomeBase
def initialize
super
remote.v = 1
end

def inc
remote.v += 1
end

def magic
do_something(remote.v)
end
end

It makes it a bit more obvious what's happening. The other benefit of this method is that it's not required to declare these with magic_accessor, as whatever #remote returns can deal magically with #/#= or #method_missing.

On Thursday, 22 November 2012 at 6:55 PM, Robert Klemme wrote:

On Thu, Nov 22, 2012 at 8:17 AM, Garthy D >> <garthy_lmkltybr@entropicsoftware.com (mailto:garthy_lmkltybr@entropicsoftware.com)> wrote:

> What I don't like with the above is that whenever I set v, I have to be
> careful to use "self.v = <value>", rather than "v = <value>" when I perform
> assignment, because the latter will just create a local variable and not
> call the "v=" method I've created. It's fine when called externally on the
> object, but any internal writes need a "self." prefix. I'm not keen on
> expecting the user to do this each time, when using "@v" equivalent is so
> much easier to use.
>
> Is there a better way to implement this, such that the user can write code
> similarly to the listing at the top? Is there some syntactic sugar I am
> unaware of that can be used instead of "self.foo"? The goal is to make the
> user API for this nice and simple- I don't mind if I have to mess about to
> make it happen, and I've already got a custom "method_missing", and adding
> code to that is no issue.

The only solution for this that I can see is to use some kind of
transaction: you open the transaction, store object state, work with
the object and at the end something inspects instance variables and
writes all changed values. But frankly, OR mappers do exist for Ruby,
ActiveRecord is just one of them. So if it is OK to fill in "RDBMS"
for "remote storage" then you could simply use that. Other than that
I'd stick with using the accessor.

Kind regards

robert

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

Hi,

If you don't mind them accessing all vars through a name, you could have an API like:

class A< SomeBase
def initialize
super
remote[:v] = 1
end

def inc
remote[:v] += 1
end

def magic
do_something(remote[:v])
end

Or:

class A< SomeBase
def initialize
super
remote.v = 1
end

def inc
remote.v += 1
end

def magic
do_something(remote.v)
end

It makes it a bit more obvious what's happening. The other benefit of this method is that it's not required to declare these with magic_accessor, as whatever #remote returns can deal magically with #/#= or #method_missing.

I'm not quite getting the remote thing -- is this a ruby-ism, a
variable, a method, what? Doing a google on "ruby remote" doesn't get
me anything that I can make sense of this.

The suggestion is essentially to create a Ruby class that uses the array ops ("" and "=") to manipulate the remote user variables, which coincidentally was in the same style as the present solution at the time of my original post. :slight_smile:

An implementation for the class Remote might look like this:

class Remote
   def initialize ...
     ...
   end

   def = a, v
     @server.sendVariableWriteMessage a, v
   end

   def a
     return @server.sendVariableReadMessage a
   end
end

And we create a remote object for each instance of A:

class A <SomeBase
   def initialize
     super
     @remote = Remote.new ...
   end
   ...

   attr_reader :remote
end

ie. each object has a remote object attached to it, and you use "remote" explicitly whenever you want to manipulate one of the remotely-stored variables. For example, the line:

remote[:v] = 1

would use the remote object, call the "=" method on it, which would cause the variable to be sent to the server.

My question is, essentially, what ways could the problem I mentioned in my original post be solved with user convenience as a goal, and this is one potential solution.

So, in answer to your question, "remote" isn't a Rubyism as such, it's just the name of the object we're using, but treating it like an array (with "" and "=" ops) is a Rubyism.

I hope this helps. :slight_smile:

Cheers,
Garth

路路路

On 23/11/12 00:16, tamouse mailing lists wrote:

On Thu, Nov 22, 2012 at 3:55 AM, Arlen Cuss<ar@len.me> wrote: