File: activipy.org

package info (click to toggle)
python-activipy 0.1-10
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 436 kB
  • sloc: python: 1,260; makefile: 171; lisp: 18; sh: 13
file content (836 lines) | stat: -rw-r--r-- 28,716 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
* Braindump

Python is an imperative language; we won't try to be "purely
functional" but "hidden state" could really screw us, so we'll try to
reduce that as much as possible.

We need:
 - Use a gdbm backed activitystreams object storage
 - For extensions, pyld
 - verifiers
 - method dispatch

DMD store should store in a dictionary like:
... with the key being the "@id" of the toplevel activitystreams
object.

#+BEGIN_SRC python
  {"asobj": {"@type": "Object",
             "@id": "uuid:d773cb99-078b-496b-b3f0-012d3ade5930",
             "blah": "blah"},
   "private": {"blah": "blah"}}
#+END_SRC

We are going to have to handle inheritance manually, because there can
be multiple types.  We can't use python's inheritance system.

We need an "ASVocab" system to operate within.  This one should have
a memoized version of the json-ld expansion of the default
activitystreams vocabulary, but it should also have a mapping of
type URIs to ASClass objects.

The store will be used separately, should provide simple store and
retrieve mechanisms.

Some complexity comes from the fact that in a "real world" system, we
don't just store and receive what's been given to us.  We need a way
to trigger application-specific hooks.

* Tasks
** TODO Add optional thunk to ASType constructor: get_default_env

Should return the default vocabulary if none specified.

** TODO Handle all @context edges
*** Brainstorming

Probably what we ought to do is enforce that everything in an ASObj
share a common @context setup.

This means, it doesn't matter what's on an @context coming into
ASObj(), we slice it off and replace it with @context after the
deepcopy_in (and all child objects, we don't need an @context at all).

But we do need to be able to "ingest foreign material", which means we
should provide an Environment.ingest() method (or .ingest_foreign())

As for how to combine multiple contexts, it can be done via an array;
see the [[http://www.w3.org/TR/json-ld/#dfn-local-context][local context]] part of the docs.

So that solves how to do things! (Though there may be some minor
question as to what to do if the application's @context is also an
array... do we merge them?  Will it work as-is?)

*** DONE Add extra_context field to Environment
    CLOSED: [2015-10-29 Thu 13:42]

**** DONE Add to init structure
     CLOSED: [2015-10-29 Thu 13:20]
**** DONE Add on expansion
     CLOSED: [2015-10-29 Thu 13:20]
**** DONE Oh wait remove on expansion
     CLOSED: [2015-10-29 Thu 13:42]

That didn't make sense, because the extra context gets added to the
asobj, so it doesn't need to be implicit.

*** TODO Handling when creating new ASObj()

Basically, strip off the existing @context recursively with
deepcopy_in, and add our context instead, if any exists.

*** TODO Environment.ingest(jsonld, imply_asvocab=True)

This should expand out a json-ld document then compact it to the
Environment's own context.

The question is, can we use the "grafted-on @context" required in the
above description, or do we have

*** TODO breaking off a child ASObj

Basically attach an @context to the json we have of it (though I guess
this will happen automatically)

** TODO Maybe rename MethodId to MethodSpec
** TODO Switch from method "object symbols" to strings?

Currently we require setting up "object symbols" which self-document
what a method does and etc, but they're also slightly unwieldy to set
up.  It will be less precise, but maybe easier, to just use strings to
represent what a method is.

** TODO Add memoization
*** TODO General memoization function
*** TODO Property memoization
** TODO Switch ASObj.__getitem__ to use deepcopy_jsobj_out

Or rather, we should specify both a deepcopy_jsobj_in and a deepcopy_jsobj_out :)

So, if we're accessing a key value pair where the value is a list of
activitystreams objects, we'd like the activitystreams objects
converted to ASObj objects as well.

*** DONE Add deepcopy_jsobj_out
    CLOSED: [2015-10-29 Thu 14:07]
*** DONE Use it in ASObj.__getitem__
    CLOSED: [2015-10-29 Thu 14:07]
*** DONE Rename deepcopy_jsobj -> deepcopy_jsobj_in
    CLOSED: [2015-10-29 Thu 14:07]

*** TODO Add tests

** TODO Add ASProp

** TODO Add demos section
*** DONE Vocab demo
    CLOSED: [2015-11-01 Sun 10:30]

RoyalCheckin or CheckUp

 - checkup:CheckIn
 - checkup:RoyalStatus
 - checkup:Coupon

*** TODO linter/validator

We can use the method dispatch system to handle this.

*** Archive                                                         :ARCHIVE:
**** DONE Easy GDBM based storage system
     CLOSED: [2015-10-28 Wed 17:17]
     :PROPERTIES:
     :ARCHIVE_TIME: 2015-10-28 Wed 17:21
     :END:
** TODO Documentation basics
*** TODO Tutorial
*** TODO Document basic "types" structure

*** Archive                                                         :ARCHIVE:
**** DONE Add sphinx basic structure
     CLOSED: [2015-10-22 Thu 13:01]
     :PROPERTIES:
     :ARCHIVE_TIME: 2015-10-28 Wed 17:21
     :END:
**** DONE Documentation structure
     CLOSED: [2015-10-28 Wed 17:17]
     :PROPERTIES:
     :ARCHIVE_TIME: 2015-10-28 Wed 17:21
     :END:

  - Intro
    - About ActiviPy
    - Tutorial
  - Core types
  - Vocabulary
  - Extending the environment
  - Advanced Examples

** TODO Rename CheckUp demo to VisitIt everywhere
*** TODO code
*** TODO docs

** TODO Make ASVocab more useful

How to do this?

We want to:
 - probably preload a json-ld context
 - Somehow make ASVocab objects useful for a 
 - make ourself more useful to ASObj objects

** TODO Tests
*** TODO Test all types.py stuff
**** TODO ASVocab
**** TODO ASObj
**** TODO ASEnvironment
**** Archive                                                        :ARCHIVE:
***** DONE ASType
      CLOSED: [2015-10-12 Mon 16:37]
      :PROPERTIES:
      :ARCHIVE_TIME: 2015-10-28 Wed 17:21
      :END:
*** TODO Basic vocabs stuff
*** Archive                                                         :ARCHIVE:
**** DONE Basic test infrastructure
     CLOSED: [2015-10-12 Mon 16:37]
     :PROPERTIES:
     :ARCHIVE_TIME: 2015-10-28 Wed 17:21
     :END:
** TODO Consider rename to Pydraulics?

After all, I'm the one who started that project, and it's abandoned...

** TODO Investigate restructuring ASType instances via metaclassing

Basically, the main reason is that we'd like to be able to do:

#+BEGIN_SRC python
help(CollectionPage)
#+END_SRC

and get the appropriate useful info.

However, it's still true that calling CollectionPage() should return a
ASObj object, not a CollectionPage() object.  Reason being that
ActivityStreams objects can have multiple "@type" fields.

** Archive                                                          :ARCHIVE:
*** DONE Add license stuff
    CLOSED: [2015-10-07 Wed 15:01]
    :PROPERTIES:
    :ARCHIVE_TIME: 2015-10-07 Wed 15:01
    :END:
**** DONE Add license files
     CLOSED: [2015-10-07 Wed 14:29]

**** DONE Add note on why both apache v2 and gplv3 to COPYING
     CLOSED: [2015-10-07 Wed 14:33]

**** DONE Add copyright headers and a note on convention
     CLOSED: [2015-10-07 Wed 15:01]


*** DONE Fill in complete vocabulary
    CLOSED: [2015-10-12 Mon 15:36]
    :PROPERTIES:
    :ARCHIVE_TIME: 2015-10-12 Mon 15:41
    :END:
*** CANCELED Switch to pyrsistent for ASObj structures?
   CLOSED: [2015-10-12 Mon 15:35]
    :PROPERTIES:
    :ARCHIVE_TIME: 2015-10-12 Mon 15:41
    :END:

https://github.com/tobgu/pyrsistent

We more or less force/fake immutability right now, and maybe it makes
more sense to just use something that *is* immutable

*UPDATE:* Canceled.  [[https://gist.github.com/datagrok/2199506][More info]] on why Pyrsistent has a promising future,
  but can't work for now.

*** CANCELED Command line test suite
   CLOSED: [2015-10-12 Mon 22:45]
    :PROPERTIES:
    :ARCHIVE_TIME: 2015-10-15 Thu 21:05
    :END:

This is [[https://github.com/evanp/a2test][its own project]] now.  See [[https://github.com/w3c-social/activipy/issues/1][this issue]].

**** Relevant parts of convo

<evanpro> paroneayea: so, a couple of questions on that
<evanpro> Does having a single package that is a producer and a consumer make
          sense? Or multiple packages?                                  [12:18]
<paroneayea> evanpro: my first goal is to make a library for the purpose of
             tests, basically along the lines of how you suggested... it'll
             just store @id's to a gdbm store.  But I'll design it in a way
             that afterwards, it can be used for something like pypump, and
             for using as2 stuff
<paroneayea> but my first goal is: fulfill the test requirements
<evanpro> Whoa!
<paroneayea> while working towards something more general
<paroneayea> gdbm is oldschool I know
<evanpro> Wait what's the GDBM for?
<evanpro> I don't understand what you need persistence for              [12:19]
<paroneayea> well it could also just be a dictionary
<evanpro> Wouldn't an AS2 library do something like
<paroneayea> I was going along with your suggestion that you have a
             command-line submission tool
<evanpro> JSON -> native language object
<evanpro> and native language object -> JSON
<paroneayea> evanpro: yes
<paroneayea> evanpro: ok well maybe it can be in-memory only            [12:20]
<paroneayea> evanpro: my main concern is get the thing working
<evanpro> 1s
<evanpro> So I was thinking that a test command-line app might look like this
<evanpro> https://gist.github.com/evanp/b49c3fc37caa21a323a1
<strugee> hey, would it be useful if I created next week's meeting page and
          filled it with the stuff on the agenda that we didn't get to?
<strugee> e.g. we missed branching models
<evanpro> strugee: YES!                                                 [12:23]
<evanpro> Nice
<paroneayea> evanpro: that might work nicely
<strugee> will do
<paroneayea> evanpro: okay, I will probably do something like that      [12:24]
<evanpro> paroneayea: and then a test driver would work like this
<evanpro> https://gist.github.com/evanp/5d80c0aa3f168465d84d
<evanpro> So that way you could call "testdriver.py dumpactivitytype.py"
                                                                        [12:25]
<evanpro> as well as "testdriver.py dumpactivitytype.rb"
<paroneayea> evanpro: ok
<paroneayea> evanpro: I see
<paroneayea> evanpro: we also want a way to show mutations              [12:26]
<paroneayea> evanpro: and side effects
<paroneayea> eg update verbs should actually update the thing in store
<evanpro> That might be too much for a data format to deal with
<paroneayea> evanpro: I mean, for the test suite
<evanpro> Yes, that's what I'm saying
<paroneayea> we want to be sure that activities can actually do the things
             they promise
<evanpro> What I'm saying is that no we don't                           [12:27]
<evanpro> When we're testing the social API, definitely
<paroneayea> evanpro: this is why I was saying that there's not much to do as
             in terms of a test suite
<evanpro> But I think an activity streams library should just parse from JSON
          and export to JSON
<paroneayea> the only thing your example checks really is that it's valid
             right?
<paroneayea> that it's json, has the right fields, in the right types
<evanpro> It checks that the activitystreams implementation library (the one
          that the dumpactivitytype.py script imports) can find the type of an
          activity                                                      [12:28]
<evanpro> I realize that it appears to be really trivial
<evanpro> But you'd need dozens of such test scripts                    [12:29]
<evanpro> dumpactivityactortype.py
<evanpro> dumpactivityactorid.py
<evanpro> That kind of thing
<paroneayea> evanpro: okay, so I'll definitely support this.
<evanpro> Another possibility is using command-line arguments
<paroneayea> evanpro: though, one of the things is, the activitystreams
             vocabulary *does* describe things with side effects
<paroneayea> I might test for that too, but I won't make it so complex that
             you can't do the simple tsts you ahve                      [12:30]
<evanpro> That's probably a fair point
<evanpro> I would really, really strongly recommend that you first publish
          your intentions for the test format
<paroneayea> evanpro: to the list?
<evanpro> And that you concentrate on the bare minimum first
<evanpro> Yes
<paroneayea> evanpro: okay I'll do that
<evanpro> to the list                                                   [12:31]
<paroneayea> evanpro: I was planning on working on deployment stuff this week,
             but it seems like this has become really urgent
<paroneayea> so I'll make it priority #1
<evanpro> So, one thing we can do when we have even a rudimentary test suite
<evanpro> Is that we can start testing libraries
<evanpro> And so we can start writing libraries                         [12:32]
<paroneayea> evanpro: right
<evanpro> We could even have a hackathon to implement in a lot of different
          languages
<evanpro> And push implementations to npm, Ruby gems, pypi, etc.
<paroneayea> evanpro: anyway, maybe now you can see why I was looking at gdbm;
             if we do have a command line test thing and we *do* promise to
             deliver tests on side effects
<paroneayea> we need some way to persist things
<paroneayea> but
<paroneayea> I agree
<paroneayea> there are tests that don't need that
<evanpro> Right, I hear you
<paroneayea> focus on the other stuff first.
<evanpro> They seem trivial but they are so important                   [12:33]
<evanpro> Probably the big thing is defining what the interface between
          testdriver script and the tested script is
<paroneayea> (and the reason why gdbm is even though it's oldschool, it's also
             dead easy to get working because it's so "dumb")
<paroneayea> evanpro: right.
<evanpro> Oh, yeah, GDBM is fine there
<evanpro> I might suggest using command-line args, too                  [12:34]
<paroneayea> evanpro: I get why you had a "don't engineer this, chris!"
             reaction though :)
<evanpro> maybe something like this
<paroneayea> er
<paroneayea> overengineer
<evanpro> <dumpscript> --activity-part actor --part-property id <filename>
<evanpro> <dumpscript> --activity-part=actor --part-property=id <filename>
                                                                        [12:35]
<evanpro> Those are crummy names but :shrug:
<evanpro> That way implementers don't have to write 50 different testing shims
<paroneayea> evanpro: I hear you
<paroneayea> evanpro: well, it may even be easier                       [12:36]
<evanpro> It may also be worthwhile to have a producer test
<paroneayea> --extract ["actor"]["@id"]
<evanpro> That takes in some parameters and outputs some JSON
<evanpro> Sure
<evanpro> I'd be a little worried about defining a query language
<evanpro> But yeah
<paroneayea> evanpro: it's probably equally complex to define a billion
             arguments
<evanpro> So a producer script might take arguments like this
<paroneayea> for the different components                               [12:37]
<evanpro> agreed!
<evanpro> <buildscript> --actor-id=urn:test:whatever --actor-name="Evan
          Prodromou" --activity-type="Like" --object-id=urn:test:whatever2
          --object-name="This terrible test"                            [12:38]
<evanpro> But yeah pretty nightmarish
<paroneayea> evanpro: so is the idea that this should spit out a
             success/failure code or
<evanpro> Oh, no!
<evanpro> It should spit out JSON!
<paroneayea> just extract the right part?
<paroneayea> okay
<paroneayea> evanpro: and it should validate, right?                    [12:39]
<evanpro> dumpscript == take JSON, just spit out some extracted part of it
<evanpro> buildscript = take params, spit out JSON
<paroneayea> oh I see.
<paroneayea> okay that makes much more sense.
<paroneayea> echoscript == take json, dump out json
<paroneayea> sorry ;)
<evanpro> dumpscript and buildscript are provided by the implementer to test
          the implementation                                            [12:40]
<evanpro> and there's a test driver to run them
<evanpro> so "testdriver dumpscript.py buildscript.py"
<evanpro> Would run all the tests
<evanpro> Or something like that
<paroneayea> hm ok....
<paroneayea> evanpro: I don't understand testdriver                     [12:41]
<paroneayea> what does it do?
<evanpro> Something like https://gist.github.com/evanp/5d80c0aa3f168465d84d



**** CANCELED dumpscript
    CLOSED: [2015-10-12 Mon 22:45]

<evanpro> dumpscript == take JSON, just spit out some extracted part of it

#+BEGIN_SRC python
  import activitystreams

  json = parseCommandLineFileArgument()

  activity = Activity.fromJSON(json)

  print activity.type
#+END_SRC

<evanpro> <dumpscript> --activity-part=actor --part-property=id <filename>

<evanpro> <dumpscript> --activity-part=actor --part-property=id <filename>
<evanpro> Those are crummy names but :shrug:
<evanpro> That way implementers don't have to write 50 different testing shims
<paroneayea> evanpro: I hear you
<paroneayea> evanpro: well, it may even be easier                       [12:36]
<evanpro> It may also be worthwhile to have a producer test
<paroneayea> --extract ["actor"]["@id"]
<evanpro> That takes in some parameters and outputs some JSON
<evanpro> Sure
<evanpro> I'd be a little worried about defining a query language
<evanpro> But yeah
<paroneayea> evanpro: it's probably equally complex to define a billion
             arguments
<evanpro> So a producer script might take arguments like this
<paroneayea> for the different components                               [12:37]
<evanpro> agreed!
<evanpro> <buildscript> --actor-id=urn:test:whatever --actor-name="Evan
          Prodromou" --activity-type="Like" --object-id=urn:test:whatever2
          --object-name="This terrible test"                            [12:38]
<evanpro> But yeah pretty nightmarish


**** CANCELED buildscript
    CLOSED: [2015-10-12 Mon 22:45]

<evanpro> buildscript = take params, spit out JSON

**** CANCELED testdriver
    CLOSED: [2015-10-12 Mon 22:45]

<evanpro> so "testdriver dumpscript.py buildscript.py"

*** DONE Hook up pyld
    CLOSED: [2015-10-20 Tue 15:56]
    :PROPERTIES:
    :ARCHIVE_TIME: 2015-10-20 Tue 15:58
    :END:
**** Brainstorm

Okay, so what do we want to do here?

 - Vocabularies might provide an "implied context".  That's the
   biggest issue, because otherwise it can be inferred unambiguously
   from expanding the document.
 - Mostly, we might not want to re-read things?

This last one is a good goal but maybe we shouldn't worry about it
immediately.

Here's the options from the JsonLdProcessor code:

#+BEGIN_SRC python
  class JsonLdProcessor(object):
      """
      A JSON-LD processor.
      """
      # [...]
      def expand(self, input_, options):
          """
          Performs JSON-LD expansion.

          :param input_: the JSON-LD input to expand.
          :param options: the options to use.
            [base] the base IRI to use.
            [expandContext] a context to expand with.
            [keepFreeFloatingNodes] True to keep free-floating nodes,
              False not to (default: False).
            [documentLoader(url)] the document loader
              (default: _default_document_loader).

          :return: the expanded JSON-LD output.
          """
#+END_SRC

 - we probably want to be able to set expandContext.
 - the documentLoader could thus possibly come with some context
   preloaded.  But that's kind of an optimization.
   
At least we know the two main steps now?

*Update:* It turns out the first of these is much simpler than we
originally were thinking!  There's only one implied context in
ActivityStreams, so we can hardcode the expandContext.

**** DONE Handle the implied context
    CLOSED: [2015-10-19 Mon 21:26]

Should be passed into the environment, but possibly built out of the
vocabulary.

**** DONE cache things in the documentLoader
    CLOSED: [2015-10-20 Tue 15:55]

The documentLoader seems to just be a function accepting a URI, and
raising JsonLdError if something goes badly.

#+BEGIN_SRC python
  {
      'contextUrl': None,
      'documentUrl': url,
      'document': data.decode('utf8')
  }
#+END_SRC

So we could write a factory function that takes a mapping of
{url: document}

#+BEGIN_SRC python
  def make_simple_loader(url_map, load_unknown_urls=True):
      def loader(url):
          # foo
          return loaded_url
      return loader
#+END_SRC

**** DONE Provide a side-effect free environment option
     CLOSED: [2015-10-20 Tue 15:55]
**** DONE Easily build expandContext and documentLoader based on supplied vocabulary?
    CLOSED: [2015-10-20 Tue 15:56]

One way or another we want to reduce the amount of data duplicated
from the building of the Environment

*** DONE Maybe rename types.py to core.py
    CLOSED: [2015-10-22 Thu 09:34]
    :PROPERTIES:
    :ARCHIVE_TIME: 2015-10-22 Thu 09:35
    :END:
*** DONE Fix how ASType.__call__() handles long vs short URIs
    CLOSED: [2015-10-21 Wed 17:39]
    :PROPERTIES:
    :ARCHIVE_TIME: 2015-10-22 Thu 09:35
    :END:
*** DONE ActivityStreams "classes"
   CLOSED: [2015-10-22 Thu 09:36]
    :PROPERTIES:
    :ARCHIVE_TIME: 2015-10-22 Thu 09:36
    :END:

Note that normal python classes can't work here.

**** DONE ASObj
     CLOSED: [2015-10-22 Thu 09:35]
***** DONE Finish all those TODO methods
      CLOSED: [2015-10-22 Thu 09:35]
***** Archive                                                       :ARCHIVE:
****** DONE Construction: Do deep copy of asjson manually
     CLOSED: [2015-10-11 Sun 11:33]
      :PROPERTIES:
      :ARCHIVE_TIME: 2015-10-12 Mon 15:41
      :END:

This way we can catch any asobj types

****** DONE Better inheritance order
     CLOSED: [2015-10-17 Sat 14:05]
      :PROPERTIES:
      :ARCHIVE_TIME: 2015-10-17 Sat 14:05
      :END:

We should do this like in the ANSI Common Lisp book, where we remove
duplicates, but we remove duplictes but keep the *last* appearance of
a "class"

**** Archive                                                        :ARCHIVE:
***** DONE Add inheritance / method dispatch system
    CLOSED: [2015-10-10 Sat 18:49]
     :PROPERTIES:
     :ARCHIVE_TIME: 2015-10-10 Sat 18:49
     :END:

This is trickier than one may think; we can't do Python style 
method resolution because an activity may have multiple types.

***** DONE Easy ASType->ASObj constructor interface
    CLOSED: [2015-10-12 Mon 15:14]
     :PROPERTIES:
     :ARCHIVE_TIME: 2015-10-12 Mon 15:41
     :END:

Something like:

#+BEGIN_SRC python
  from activipy import vocab

  root_beer_note = vocab.Create(
      actor=vocab.Person(
          "http://tsyesika.co.uk",
          displayName="Jessica Tallon"),
      to=["acct:cwebber@identi.ca"],
      object=vocab.Note(
          "http://tsyesika.co.uk/chat/sup-yo/",
          content="Up for some root beer floats?"))
#+END_SRC

This should be able to flow pretty naturally out of our types.py
interface.

*** DONE "environment" w/ method dispatch and object sugar
    CLOSED: [2015-10-26 Mon 13:49]
    :PROPERTIES:
    :ARCHIVE_TIME: 2015-10-26 Mon 13:49
    :END:

**** Brainstorm

So here's how this thing works.

There's an environment, which has a mapping between tuples of
(method_symbol, Vocab) and method_to_call.

#+BEGIN_SRC python
  #                    method name    description    invocation method
  save = Method("save", "Save things", handle_one)
  gather_something = Method("gather_something", "Accrues some info", handle_map)

  myenv = Enviroment(
     mapping={
         (save, Note): note_save,
         (save, Object): basic_save,
  })

  handle_one(myobj, save, db)
#+END_SRC

This way, using the inheritance_chain() method, we can handle
various types of method handling:

 - handle_one
 - handle_map
 - handle_fold

However, we have enough metadata here to provide some sugar.

#+BEGIN_SRC python
  myenv = Environment(
    mapping={bla bla},
    vocab=vocab)

  activity = Environment.c.Activity("http://oh/snap")
  activity.m.save(db)
  # or maybe even just activity.save()
#+END_SRC

This would have to mean that ASObj gets a method dispatch keyword
option on construction, which might be a-ok.

I think this is a pretty good approach.

**** DONE Add Environment and method dispatch
     CLOSED: [2015-10-26 Mon 13:48]
**** DONE Add vocabulary + method-class sugar
     CLOSED: [2015-10-26 Mon 13:49]
**** Archive                                                        :ARCHIVE:
***** DONE Clean up method dispatch plan based on convo w/ steve
    CLOSED: [2015-10-15 Thu 13:29]
     :PROPERTIES:
     :ARCHIVE_TIME: 2015-10-15 Thu 13:31
     :END:

#+BEGIN_SRC python
  save_object = Method("save things", "handle_one")

  myenv = Enviroment(
     mapping={
         (save_object, Note): note_save,
         })

  handle_one(myobj, "save_object", db)
  handle_one(myobj, save_object, db)

  # more pythonic optional interface
  # a bit leaky though
  myenv = MetaEnviroment(
     mapping={
         (save_object, Note): note_save,
         }
      vocab=[BasicVocab]
  )

  myenv.Person("foo")
  Person()
#+END_SRC

*** CANCELED Pass environment into methods?
   CLOSED: [2015-10-28 Wed 17:16]
    :PROPERTIES:
    :ARCHIVE_TIME: 2015-10-28 Wed 17:19
    :END:

Should methods be able to themselves take advantage of method dispatch?
If so, they will need "env" as first argument.

*** DONE Add ASObj.type_astype()
    CLOSED: [2015-10-28 Wed 17:17]
    :PROPERTIES:
    :ARCHIVE_TIME: 2015-10-28 Wed 17:19
    :END:

**** Brainstorm

Here's the problem.

Assume we made an activity like this:

#+BEGIN_SRC python
ROOT_BEER_NOTE_VOCAB = vocab.Create(
    "http://tsyesika.co.uk/act/foo-id-here/",
    actor=vocab.Person(
        "http://tsyesika.co.uk/",
        displayName="Jessica Tallon"),
    to=["acct:cwebber@identi.ca",
        "acct:justaguy@rhiaro.co.uk"],
    object=vocab.Note(
        "htp://tsyesika.co.uk/chat/sup-yo/",
        content="Up for some root beer floats?"))
#+END_SRC

Now assume we made one like this:

#+BEGIN_SRC python
ROOT_BEER_NOTE_JSOBJ = types.ASObj({
    "@type": "Create",
    "@id": "http://tsyesika.co.uk/act/foo-id-here/",
    "actor": {
        "@type": "Person",
        "@id": "http://tsyesika.co.uk/",
        "displayName": "Jessica Tallon"},
    "to": ["acct:cwebber@identi.ca",
           "acct:justaguy@rhiaro.co.uk"],
    "object": {
        "@type": "Note",
        "@id": "htp://tsyesika.co.uk/chat/sup-yo/",
        "content": "Up for some root beer floats?"}})
#+END_SRC

Now even worse:

#+BEGIN_SRC python
  ROOT_BEER_NOTE_JSOBJ = types.ASObj({
      # AAAAAAAAAAA
      "@type": "http://www.w3.org/ns/activitystreams#Create", 
      "@id": "http://tsyesika.co.uk/act/foo-id-here/",
      "actor": {
          "@type": "Person",
          "@id": "http://tsyesika.co.uk/",
          "displayName": "Jessica Tallon"},
      "to": ["acct:cwebber@identi.ca",
             "acct:justaguy@rhiaro.co.uk"],
      "object": {
          "@type": "Note",
          "@id": "htp://tsyesika.co.uk/chat/sup-yo/",
          "content": "Up for some root beer floats?"}})
#+END_SRC

So...
 - we really need to know about the whole set of vocabularies in order
   to do ASObj.type_astype()
 - Obviously, we also need to for method dispatch also
 - It could be then that we don't load ASObj.vocab, but ASObj.env
 - Also, in general you can always do env.asobj_astypes(asobj)
 - Thus, we should also provide env.asobj_method(asobj, method_symbol)
 - Which means also, more obviously, and as a precedent, we must
   provide Environment.asobj_astype_chain(asobj)!

This also means that users should, in general, not use
ASObj.type_astype(), unless they're using the "sugar" edition
which comes from supplying an environment.

We might want to also provide an expanded=True argument to some of those methods.

OR, maybe we can do "cheapest available" determination of an ASType.

What are the ways we might go about pulling down an ASType?

 - By short ID... but this requires this short ID be marked "safe"
   for short expansion
 - By already known URI
 - By json-ld examination (most expensive!)

Do we really want an expand=None?  Maybe that's kind of dumb

**** DONE From short id
    CLOSED: [2015-10-28 Wed 17:17]

The question is, where do we mark whether its safe to consider the
short_id as a safe representation from?  Is it in the environment
or in the vocab?

The vocab may make sense because we could do a
shortids=load_from_vocabs((Vocab1, None), (GMGVocab, "gmg:"))

**** DONE From known URI
     CLOSED: [2015-10-28 Wed 17:17]
**** DONE By json-ld examination
     CLOSED: [2015-10-28 Wed 17:17]