Hi,
Here is a published SNAPSHOT DRAFT of what I have so far for the tutorial.
http://tinkerpop.apache.org/docs/3.1.3-SNAPSHOT/tutorials/gremlin-language-variants/
I've asked Ketrina to do a new graphic for this. It will be CrAzY.
The gremlin-jython.py link is broken as I didn't do a full doc build. Its here
to look at if you are interested:
https://github.com/apache/incubator-tinkerpop/blob/TINKERPOP-1232/docs/static/resources/gremlin-jython.py
Marko.
http://markorodriguez.com
On Apr 19, 2016, at 10:08 PM, 8trk <[email protected]> wrote:
> Ha. That is very cool. You can easily just rewrite that for PHP and probably
> Ruby too and have working native interfaces.
>
> I updated my Gist to work with your examples. I had to update Gremlinpy
> because I didn’t define __ correctly (thanks! this was a fun challenge).
>
> https://gist.github.com/emehrkay/68a9e64789826f6a59e8b5c837dd6ce4
> <https://gist.github.com/emehrkay/68a9e64789826f6a59e8b5c837dd6ce4>
>
>
>> On Apr 19, 2016, at 11:55 PM, Marko Rodriguez <[email protected]> wrote:
>>
>> Hi,
>>
>>> I think adhering to the Gremlin-Java interface is a great idea exactly for
>>> the reasons that you stated.
>>> The main reason that I didn’t map one-to-one with the native interface is
>>> because I wasn’t too sure how to do so, I knew that there was a lot of
>>> method overloading which isn’t possible in either of the languages that I
>>> wrote this in (Python/PHP), and I figured this approach would be more
>>> flexible with regard to changes in the language (to make it TP3 all I had
>>> to do was define all of the predicates check for them when they’re passed
>>> into functions).
>>
>> Check this out. Here is a Groovy script the generates the Python traversal
>> class.
>>
>> https://gist.github.com/okram/940adc02834a97a7187d3da57cbf3227
>> - super simple.
>>
>> Thus, no need to fat finger anything in and you know you have every method
>> implemented. Moreover, every release, just generate the Python class by
>> running this script in the Gremlin Console. And it just works:
>>
>>>>> g.V().has("name","marko")
>> g.V().has("name", "marko")
>>>>> g.V().has("person","name","marko")
>> g.V().has("person", "name", "marko")
>>>>> g.V().where(out("knows"))
>> g.V().where(__.out("knows"))
>>>>> g.V()._as("a").out("created")._as("b").where(_as("a").out("knows"))
>> g.V().as("a").out("created").as("b").where(__.as("a").out("knows"))
>>>>> g.V().match(_as("a").out("knows")._as("b"),
>>>>> _as("b").out("knows")._as("a"))
>> g.V().match(__.as("a").out("knows").as("b"), __.as("b").out("knows").as("a"))
>>>>> g.V().hasLabel("person").has("age",gt(30)).out("created","knows").name
>> g.V().hasLabel("person").has("age", P.gt(30)).out("created",
>> "knows").values("name")
>>
>>
>>> The more that I think about it, I think that Gremlinpy’s aim was to be able
>>> to write Groovy in Python. That is the main reason why I didn’t choose just
>>> straight-up string concatenation — I needed to be able to do things like if
>>> clauses or closures or really compounded queries. (In Gizmo, my OGM, I’ve
>>> built some pretty dense queries to send to the Gremlin server).
>>
>> Yea, the closures are the hard part. I saw that in Python you can walk the
>> syntax tree of a closure :) … nasty.
>>
>>> Your approach is clearly closer to to Gremlin-Java interface and we should
>>> probably use some variant of it going forward. I quickly took that
>>> interface and used Gremlinpy to handle all of the processing as seen in
>>> this gist: https://gist.github.com/emehrkay/68a9e64789826f6a59e8b5c837dd6ce4
>>
>> Interesting. See how it does with my auto-code generator. Also, I want to
>> steal your P, T constructs as I think you do that better in Gremlinpy.
>>
>> Marko.
>>
>> http://markorodriguez.com
>>
>>>
>>>
>>>> On Apr 19, 2016, at 10:54 PM, Marko Rodriguez <[email protected]> wrote:
>>>>
>>>> Hi,
>>>>
>>>> Sweet -- your dev@ mail works now.
>>>>
>>>>> I think you are on to something with this code example. Gremlinpy does
>>>>> this, but a bit differently. It uses Python’s magic methods to
>>>>> dynamically build a linked list.
>>>>>
>>>>> So when you do something like
>>>>>
>>>>> g = Gremlin()
>>>>> g.function()
>>>>>
>>>>> It creates simply adds an gremlinpy.gremlin.Function object to the queue.
>>>>> That object has the parameters to send once the linked list is converted
>>>>> to a string.
>>>>
>>>> Why would you create a queue and not just concatenate a String?
>>>>
>>>>> Check out the readme for a few more examples (it can do things like add
>>>>> pre-defined statements to the chain, nesting Gremlin instances, and
>>>>> manually binding params) https://github.com/emehrkay/gremlinpy
>>>>> <https://github.com/emehrkay/gremlinpy>
>>>>
>>>> Ah, parameter bindings. Hmm…
>>>>
>>>>> I think that a very simple linked list build with a fluid interface and
>>>>> few predefined object types may be a good approach to defining a native
>>>>> way to represent a Gremlin query.
>>>>>
>>>>> What do you think?
>>>>
>>>> It would be really clean if there was GraphTraversalSource,
>>>> GraphTraversal, and __ Traversal without any "extra methods." In Gremlinpy
>>>> README I see lots of other methods off of "g" that are not Gremlin-Java
>>>> methods. It would be cool if it was a direct map of Gremlin-Java (like
>>>> Gremlin-Groovy and Gremlin-Scala). Where the only deviations are things
>>>> like _in(), _as(), etc and any nifty language tricks like g.V().name or
>>>> g.V().out()[0:10]. This way, we instill in the designers that any Gremlin
>>>> language variant should be "identical," where (within reason) the docs for
>>>> Gremlin-Java are just as useful to Gremlinpy people. Furthermore, by
>>>> stressing this, we ensure that variants don't deviate and go down their
>>>> own syntax/constructs path. For instance, I see g.v(12) instead of
>>>> g.V(12). When a Gremlin language variant wants to do something new, we
>>>> should argue -- "submit a PR to Gremlin-Java w/ your desired addition" as
>>>> Apache's Gremlin-Java should be considered the standard/idiomatic
>>>> representation of Gremlin.
>>>>
>>>> Finally, it would be cool to have a tool that introspected on Gremlin-Java
>>>> and verified that Gremlinpy had all the methods implemented. Another thing
>>>> to stress to language variant designers -- make sure you are in sync with
>>>> every version so write a test case that does such introspection.
>>>>
>>>> Thoughts?,
>>>> Marko.
>>>>
>>>> http://markorodriguez.com
>>>>
>>>>
>>>>
>>>>
>>>>>
>>>>>> On Apr 19, 2016, at 10:19 PM, Marko Rodriguez <[email protected]>
>>>>>> wrote:
>>>>>>
>>>>>> Hello,
>>>>>>
>>>>>> Okay, so I got into a groove. Here is Python->Gremlin-Groovy(String).
>>>>>> This is pure Python -- nothing Jython going on here.
>>>>>>
>>>>>> https://gist.github.com/okram/4705fed038dde673f4c5323416899992
>>>>>>
>>>>>> Here it is in action:
>>>>>>
>>>>>> # create a traversal source (stupid class name, I know)
>>>>>>>>> g = PythonStringGraphTraversalSource("g")
>>>>>>
>>>>>> # simple warmup
>>>>>>>>> g.V().has("name","marko")
>>>>>> g.V().has("name", "marko")
>>>>>>
>>>>>> # one has()-method, but varargs parsing is smart
>>>>>>>>> g.V().has("person","name","marko")
>>>>>> g.V().has("person", "name", "marko")
>>>>>>
>>>>>> # strings and numbers mixed
>>>>>>>>> g.V().has("person","age",32)
>>>>>> g.V().has("person", "age", 32)
>>>>>>
>>>>>> # nested anonymous traversal
>>>>>>>>> g.V().where(out("knows"))
>>>>>> g.V().where(__.out("knows"))
>>>>>>
>>>>>> # as() is reserved in Python, so _as() is used.
>>>>>>>>> g.V()._as("a").out("created")._as("b").where(_as("a").out("knows"))
>>>>>> g.V().as("a").out("created").as("b").where(__.as("a").out("knows"))
>>>>>>
>>>>>> # multi-traversal match()
>>>>>>>>> g.V().match(_as("a").out("knows")._as("b"),
>>>>>>>>> _as("b").out("knows")._as("a"))
>>>>>> g.V().match(__.as("a").out("knows").as("b"),
>>>>>> __.as("b").out("knows").as("a"))
>>>>>>
>>>>>> # P-predicates and .name-sugar (attribute access interception)
>>>>>>>>> g.V().hasLabel("person").has("age",gt(30)).out("created","knows").name
>>>>>> g.V().hasLabel("person").has("age", P.gt(30)).out("created",
>>>>>> "knows").values("name")
>>>>>>
>>>>>> # smart about boolean conversion
>>>>>>>>> g.V().valueMap(True,"name","age")
>>>>>> g.V().valueMap(true, "name", "age")
>>>>>>
>>>>>> # lambdas -- ghetto as its not a Python lambda, but a Groovy lambda
>>>>>> string
>>>>>>>>> g.V().map('it.get().value("name")')
>>>>>> g.V().map(it.get().value("name"))
>>>>>>
>>>>>> What other constructs are there? I think thats it… Everything else from
>>>>>> here is just fat fingering in all the methods. Then, from there you use
>>>>>> David Brown's GremlinClient (https://github.com/davebshow/gremlinclient)
>>>>>> to shuffle the string across the network to GremlinServer and get back
>>>>>> results. I suppose there needs to be some sort of .submit() method ? ….
>>>>>> hmmm… wondering if .next()/hasNext() iterator methods can be used to
>>>>>> submit automagically and then it feels JUST like Gremlin-Java.
>>>>>>
>>>>>> @Mark: This is what Gremlinpy should do, no?
>>>>>> @Dylan: Can you find any Gremlin syntax hole I'm missing that isn't
>>>>>> solvable with the current espoused pattern?
>>>>>>
>>>>>> Good, right?
>>>>>> Marko.
>>>>>>
>>>>>> http://markorodriguez.com
>>>>>>
>>>>>> On Apr 19, 2016, at 4:51 PM, Marko Rodriguez <[email protected]>
>>>>>> wrote:
>>>>>>
>>>>>>> Hi,
>>>>>>>
>>>>>>> Done for the night. Here is PythonStringGraphTraversal.
>>>>>>>
>>>>>>> https://gist.github.com/okram/4705fed038dde673f4c5323416899992
>>>>>>>
>>>>>>> ??? Cool?
>>>>>>>
>>>>>>> Marko.
>>>>>>>
>>>>>>> http://markorodriguez.com
>>>>>>>
>>>>>>> On Apr 19, 2016, at 4:28 PM, Marko Rodriguez <[email protected]>
>>>>>>> wrote:
>>>>>>>
>>>>>>>> Hi,
>>>>>>>>
>>>>>>>> So I "learned" Python and am able to do a Python class wrapper around
>>>>>>>> GraphTraversal.
>>>>>>>>
>>>>>>>> https://gist.github.com/okram/1a0c5f6b65a4b70c558537e5eeaad429
>>>>>>>>
>>>>>>>> Its crazy, it "just works" -- with __ static methods and all.
>>>>>>>>
>>>>>>>> The reason I wanted to create a wrapper is because I want to use
>>>>>>>> Python-specific language constructs and not only Gremlin-Java. What
>>>>>>>> those specific language constructs are, I don't know as I don't know
>>>>>>>> Python :). Moreover, this shell of a wrapper will be used for the JNI
>>>>>>>> and String construction models. Right?
>>>>>>>>
>>>>>>>>>>> g = PythonGraphTraversalSource(graph)
>>>>>>>>>>> g
>>>>>>>> graphtraversalsource[tinkergraph[vertices:6 edges:6], standard]
>>>>>>>>>>> g.V()
>>>>>>>> [GraphStep(vertex,[])]
>>>>>>>>>>> g.V().toList()
>>>>>>>> [v[1], v[2], v[3], v[4], v[5], v[6]]
>>>>>>>>>>> g.V().where(__.out("created")).values("name").toList()
>>>>>>>> [marko, josh, peter]
>>>>>>>>>>>
>>>>>>>>
>>>>>>>> Even valueMap() which takes var args of different types works.
>>>>>>>>
>>>>>>>>>>> g.V().valueMap()
>>>>>>>> [GraphStep(vertex,[]), PropertyMapStep(value)]
>>>>>>>>>>> g.V().valueMap().toList()
>>>>>>>> [{name=[marko], age=[29]}, {name=[vadas], age=[27]}, {name=[lop],
>>>>>>>> lang=[java]}, {name=[josh], age=[32]}, {name=[ripple], lang=[java]},
>>>>>>>> {name=[peter], age=[35]}]
>>>>>>>>>>> g.V().valueMap("name").toList()
>>>>>>>> [{name=[marko]}, {name=[vadas]}, {name=[lop]}, {name=[josh]},
>>>>>>>> {name=[ripple]}, {name=[peter]}]
>>>>>>>>>>> g.V().valueMap(True,"name").toList()
>>>>>>>> [{label=person, name=[marko], id=1}, {label=person, name=[vadas],
>>>>>>>> id=2}, {label=software, name=[lop], id=3}, {label=person, name=[josh],
>>>>>>>> id=4}, {label=software, name=[ripple], id=5}, {label=person,
>>>>>>>> name=[peter], id=6}]
>>>>>>>>>>>
>>>>>>>>
>>>>>>>> Easy peasy lemon squeezy or is there something fundamental I'm missing?
>>>>>>>>
>>>>>>>> Marko.
>>>>>>>>
>>>>>>>> http://markorodriguez.com
>>>>>>>>
>>>>>>>> On Apr 19, 2016, at 2:58 PM, Marko Rodriguez <[email protected]>
>>>>>>>> wrote:
>>>>>>>>
>>>>>>>>> Hi,
>>>>>>>>>
>>>>>>>>> So I downloaded and installed Jython 2.7.0.
>>>>>>>>>
>>>>>>>>> This how easy it was to get Gremlin working in Jython.
>>>>>>>>>
>>>>>>>>> import sys
>>>>>>>>> sys.path.append("/Users/marko/software/tinkerpop/tinkerpop3/gremlin-console/target/apache-gremlin-console-3.2.1-SNAPSHOT-standalone/lib/commons-codec-1.9.jar")
>>>>>>>>> sys.path.append("/Users/marko/software/tinkerpop/tinkerpop3/gremlin-console/target/apache-gremlin-console-3.2.1-SNAPSHOT-standalone/lib/commons-configuration-1.10.jar")
>>>>>>>>> … lots of jars to add
>>>>>>>>> sys.path.append("/Users/marko/software/tinkerpop/tinkerpop3/gremlin-console/target/apache-gremlin-console-3.2.1-SNAPSHOT-standalone/ext/tinkergraph-gremlin/lib/tinkergraph-gremlin-3.2.1-SNAPSHOT.jar")
>>>>>>>>>
>>>>>>>>> from org.apache.tinkerpop.gremlin.tinkergraph.structure import
>>>>>>>>> TinkerFactory
>>>>>>>>> graph = TinkerFactory.createModern()
>>>>>>>>> g = graph.traversal()
>>>>>>>>> g
>>>>>>>>> g.V().hasLabel("person").out("knows").out("created")
>>>>>>>>> g.V().hasLabel("person").out("knows").out("created").toList()
>>>>>>>>>
>>>>>>>>> Then, the output looks like this:
>>>>>>>>>
>>>>>>>>>>>> from org.apache.tinkerpop.gremlin.tinkergraph.structure import
>>>>>>>>>>>> TinkerFactory
>>>>>>>>>>>> graph = TinkerFactory.createModern()
>>>>>>>>>>>> g = graph.traversal()
>>>>>>>>>>>> g
>>>>>>>>> graphtraversalsource[tinkergraph[vertices:6 edges:6], standard]
>>>>>>>>>>>> g.V().hasLabel("person").out("knows").out("created")
>>>>>>>>> [GraphStep(vertex,[]), HasStep([~label.eq(person)]),
>>>>>>>>> VertexStep(OUT,[knows],vertex), VertexStep(OUT,[created],vertex)]
>>>>>>>>>>>> g.V().hasLabel("person").out("knows").out("created").toList()
>>>>>>>>> [v[5], v[3]]
>>>>>>>>>
>>>>>>>>> Note that, of course, Jython's command line doesn't auto-iterate
>>>>>>>>> traversals. Besides that -- sheez, that was simple.
>>>>>>>>>
>>>>>>>>> The trick now is to use Jython idioms to make Gremlin-Jython be
>>>>>>>>> comfortable to Python users…
>>>>>>>>>
>>>>>>>>> Marko.
>>>>>>>>>
>>>>>>>>> http://markorodriguez.com
>>>>>>>>>
>>>>>>>>> On Apr 19, 2016, at 11:43 AM, Marko Rodriguez <[email protected]>
>>>>>>>>> wrote:
>>>>>>>>>
>>>>>>>>>> Hi,
>>>>>>>>>>
>>>>>>>>>> So I just pushed:
>>>>>>>>>>
>>>>>>>>>> https://git1-us-west.apache.org/repos/asf?p=incubator-tinkerpop.git;a=commitdiff;h=0beae616
>>>>>>>>>>
>>>>>>>>>> This should help provide the scaffolding for the tutorial. Given
>>>>>>>>>> that I know nothing about Python, I think my contributions start to
>>>>>>>>>> fall off significantly here. :) … Well, I can help and write more
>>>>>>>>>> text, I just don't know how to use Jython, Python idioms, Gremlinpy,
>>>>>>>>>> etc…..
>>>>>>>>>>
>>>>>>>>>> @Mark/Dylan: If you want to build the tutorial and look at it, you
>>>>>>>>>> simple do:
>>>>>>>>>>
>>>>>>>>>> $ bin/process-docs.sh --dryRun
>>>>>>>>>>
>>>>>>>>>> And then for me, the URI to which I point my browser for the
>>>>>>>>>> index.html on my local computer is:
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> file:///Users/marko/software/tinkerpop/tinkerpop3/target/docs/htmlsingle/tutorials/gremlin-language-variants/index.html
>>>>>>>>>>
>>>>>>>>>> Marko.
>>>>>>>>>>
>>>>>>>>>> http://markorodriguez.com
>>>>>>>>>>
>>>>>>>>>> On Apr 19, 2016, at 9:16 AM, Marko Rodriguez <[email protected]>
>>>>>>>>>> wrote:
>>>>>>>>>>
>>>>>>>>>>> Hello (NOTE: I dropped gremlin-users@),
>>>>>>>>>>>
>>>>>>>>>>> Thank you Stephen. Its crazy how simple that is :D.
>>>>>>>>>>> https://twitter.com/apachetinkerpop/status/722432843360546816
>>>>>>>>>>>
>>>>>>>>>>> So Mark, now your fork's TINKERPOP-1232/ branch and
>>>>>>>>>>> https://github.com/apache/incubator-tinkerpop/tree/TINKERPOP-1232
>>>>>>>>>>> exist and we can keep them sync'd accordingly as we develop this
>>>>>>>>>>> tutorial. When we feel that the tutorial is ready for primetime, we
>>>>>>>>>>> will issue a PR to have it merged into tp31/ (and thus, up merged
>>>>>>>>>>> to master/).
>>>>>>>>>>>
>>>>>>>>>>> Where do we go from here? I think this is a good opportunity to
>>>>>>>>>>> work both on Gremlinpy and the tutorial. Can we make Gremlinpy as
>>>>>>>>>>> true to the spirit of "host language embedding" as possible? In
>>>>>>>>>>> doing so, can we explain how we did it so other language providers
>>>>>>>>>>> can learn the best practices?
>>>>>>>>>>>
>>>>>>>>>>> In the tutorial we have 3 models we want to promote:
>>>>>>>>>>>
>>>>>>>>>>> 1. Jython
>>>>>>>>>>> 2. Python JINI
>>>>>>>>>>> 3. Python String
>>>>>>>>>>>
>>>>>>>>>>> (1) is easy to knock off. In fact, we should ask Michael Pollmeier
>>>>>>>>>>> for advice here given his work on Gremlin-Scala. (2) -- ?? do you
>>>>>>>>>>> know how do this? If so, it should be only fairly more difficult
>>>>>>>>>>> than (1). Finally, (3) is the big win and where I think most of the
>>>>>>>>>>> work both in the tutorial and in Gremlinpy will happen.
>>>>>>>>>>>
>>>>>>>>>>> How do you propose we proceed?
>>>>>>>>>>>
>>>>>>>>>>> Thank you,
>>>>>>>>>>> Marko.
>>>>>>>>>>>
>>>>>>>>>>> http://markorodriguez.com
>>>>>>>>>>>
>>>>>>>>>>> On Apr 19, 2016, at 8:24 AM, Stephen Mallette
>>>>>>>>>>> <[email protected]> wrote:
>>>>>>>>>>>
>>>>>>>>>>>> ok - done:
>>>>>>>>>>>> https://github.com/apache/incubator-tinkerpop/tree/TINKERPOP-1232
>>>>>>>>>>>>
>>>>>>>>>>>> On Tue, Apr 19, 2016 at 9:41 AM, Marko Rodriguez
>>>>>>>>>>>> <[email protected]> wrote:
>>>>>>>>>>>> Hello,
>>>>>>>>>>>>
>>>>>>>>>>>> *** Mark, if you are not on dev@tinkerpop, I would recommend
>>>>>>>>>>>> joining that as I will drop gremlin-users@ from communication on
>>>>>>>>>>>> this ticket from here on out. ***
>>>>>>>>>>>>
>>>>>>>>>>>> @Stephen: Mark forked the TinkerPop repository to his GitHub
>>>>>>>>>>>> account. I believe he gave you access as well as me.
>>>>>>>>>>>>
>>>>>>>>>>>> Can you create a new stub tutorial for Mark+Dylan+me? (Moving
>>>>>>>>>>>> forward, I will learn how to do it from your one commit).
>>>>>>>>>>>>
>>>>>>>>>>>> gremlin-language-variants/
>>>>>>>>>>>>
>>>>>>>>>>>> After that Mark+Dylan+me will go to town on:
>>>>>>>>>>>> https://issues.apache.org/jira/browse/TINKERPOP-1232
>>>>>>>>>>>>
>>>>>>>>>>>> Thank you,
>>>>>>>>>>>> Marko.
>>>>>>>>>>>>
>>>>>>>>>>>> http://markorodriguez.com
>>>>>>>>>>>>
>>>>>>>>>>>> Begin forwarded message:
>>>>>>>>>>>>
>>>>>>>>>>>>> From: Mark Henderson <[email protected]>
>>>>>>>>>>>>> Subject: emehrkay added you to incubator-tinkerpop
>>>>>>>>>>>>> Date: April 15, 2016 10:04:54 AM MDT
>>>>>>>>>>>>> To: "Marko A. Rodriguez" <[email protected]>
>>>>>>>>>>>>>
>>>>>>>>>>>>> You can now push to this repository.
>>>>>>>>>>>>>
>>>>>>>>>>>>> ---
>>>>>>>>>>>>> View it on GitHub:
>>>>>>>>>>>>> https://github.com/emehrkay/incubator-tinkerpop
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>> --
>>>>>>>>>>>> You received this message because you are subscribed to the Google
>>>>>>>>>>>> Groups "Gremlin-users" group.
>>>>>>>>>>>> To unsubscribe from this group and stop receiving emails from it,
>>>>>>>>>>>> send an email to [email protected].
>>>>>>>>>>>> To view this discussion on the web visit
>>>>>>>>>>>> https://groups.google.com/d/msgid/gremlin-users/18A7D2FD-B9B1-4DC9-980B-66A6A8F9C7C8%40gmail.com.
>>>>>>>>>>>> For more options, visit https://groups.google.com/d/optout.
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>> --
>>>>>>>>>>>> You received this message because you are subscribed to the Google
>>>>>>>>>>>> Groups "Gremlin-users" group.
>>>>>>>>>>>> To unsubscribe from this group and stop receiving emails from it,
>>>>>>>>>>>> send an email to [email protected].
>>>>>>>>>>>> To view this discussion on the web visit
>>>>>>>>>>>> https://groups.google.com/d/msgid/gremlin-users/CAA-H43990bN1xrtkL%2BWW4Z%3DKY-bhamBuunpzmYcqVxniyv3NOw%40mail.gmail.com.
>>>>>>>>>>>> For more options, visit https://groups.google.com/d/optout.
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>
>>>>>>>
>>>>>>
>>>>>
>>>>
>>>
>>
>