pacer-jogger 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ 0.0.3
2
+ =====
3
+
4
+ - fixed non working two argument traversals
@@ -1,31 +1,32 @@
1
- # Jogger - almost like named scopes
1
+ Jogger - almost like named_scopes
2
+ =================================
2
3
 
3
- Jogger is a JRuby library that enables lazy people to do very expressive graph traversals with the great [pacer gem](https://github.com/pangloss/pacer). If you don't know what the pacer gem is, you should probably not be here and check pacer out first.
4
+ Jogger is a JRuby library that enables lazy people to do very expressive graph traversals with the great [pacer gem](https://github.com/pangloss/pacer). If you don't know what the pacer gem is, you should probably not be here and check pacer out first. And don't miss the pacer section at the end of text.
4
5
 
5
- # What does Jogger do? TL;DR
6
-
7
- Remember the _named scopes_ from back in the days when you were using rails? They were handy, weren't they? Jogger gives you _named traversals_ and is a little bit like named scopes. Jogger groups multiple pacer traversals together and give them a name. Pacer traversals are are like pipes. What are pipes? [Pipes are great!](http://markorodriguez.com/2011/08/03/on-the-nature-of-pipes/)!
6
+ Remember the _named_scopes_ from back in the days when you were using rails? Jogger gives you _named traversals_ and is a little bit like named scopes. Jogger groups multiple pacer traversals together and give them a name. Pacer traversals are are like pipes. What are pipes? [Pipes are great!](http://markorodriguez.com/2011/08/03/on-the-nature-of-pipes/)!
8
7
 
9
8
  The most important conceptual difference is, that the order in which named traversals are called matter, while it usually doesn't matter in which order you call named scopes.
10
9
 
11
- Jogger does two things:
10
+ TL; DR: Jogger does two things:
11
+ =======================
12
12
 
13
13
  1. Keep the current pacer traversal in an instance variable and allow for method chaining as well as changing the internal state of the traversal
14
14
  2. Allows you to group together parts of a traversal (single pipes or groups of them) and give them a name. Named traversals. Helps to stay DRY.
15
15
 
16
- The former is really just a syntax thing, whereas the latter can help you a great deal modeling semantics of your business logic as parts of traversals. These sentences confuse me, so I will give you a TL;DR gif followed by some hands on examples.
16
+ The former is really just a syntax thing, whereas the latter can help you a great deal modeling semantics of your business logic as parts of traversals. Let's jog!
17
17
 
18
- ![TL;DR](http://dl.dropbox.com/u/1953503/gifs/berneydidnotread.gif)
18
+ ![Run](http://dl.dropbox.com/u/1953503/gifs/vizPZ.gif)
19
19
 
20
20
 
21
- # Feature #1 (not so important): keep the current traversal
21
+ Feature #1: keep the current traversal
22
+ --------------------------------------
22
23
 
23
24
  To demonstrate why point 1) in the list above can be useful, look at this traversal. It helps me find out what movies my female friends like the most, so I can impress them in a conversation:
24
25
 
25
26
  t = my_pacer_vertex.in(:friends)
26
- t = t.filter{|v| v.properties['gender'] == 'female}
27
+ t = t.filter(gender: 'female')
27
28
  t = t.out(:likes)
28
- t = t.filter{ |v| v.properties['type'] == 'Movie' }
29
+ t = t.filter(type: 'Movie')
29
30
  t = t.sort_by{ |v, c| -c }
30
31
  t = t.group_count{ |v| v }
31
32
 
@@ -35,19 +36,20 @@ So here's the Jogger way of expressing this:
35
36
 
36
37
  t = Jogger.new(my_pacer_vertex)
37
38
  t.in(:friends)
38
- t.filter{ |v| v.properties['gender'] == 'female' }
39
+ t.filter(gender: 'female')
39
40
  t.out(:likes)
40
- t.filter{ |v| v.properties['type'] == 'Movie' }
41
+ t.filter(type: 'Movie')
41
42
  t.sort_by{ |v, c| -c }
42
43
  t.group_count{ |v| v }
43
44
 
44
45
  See what I did there? Jogger keeps the current pacer traversal and forwards all method calls to that traversal, and then returns itself. So you could also write (in jogger as well as pacer):
45
46
 
46
- Jogger.new(my_pacer_node).in(:friends).filter{ }.out(:likes).group_count{…}
47
+ Jogger.new(my_pacer_node).in(:friends).filter().out(:likes).group_count{…}
47
48
 
48
49
  Just saying, you can chain your methods, but I don't like it cause I can only focus on 72 characters per line at max. If you want the current traversal, just call `result` on your Jogger instance.
49
50
 
50
- ## Feature #2 (pretty useful): Named Traversals
51
+ Feature #2: Named Traversals
52
+ ----------------------------
51
53
 
52
54
  So that traversal above, traversing from a node to all its friends, is pretty simple, but it could be simpler. Especially if it does things that you want to reuse in many other places. How cool would it be if I just had to write this:
53
55
 
@@ -55,7 +57,7 @@ So that traversal above, traversing from a node to all its friends, is pretty si
55
57
  t.friends(:female)
56
58
  t.top_list(:movies)
57
59
 
58
- No problem. Just define named traversals that aggregate different pipes and give them a name:
60
+ No problem. Just define named traversals that aggregate different pipes and give them a name. You have to put your traversal into Jogger's `NamedTraverals` module.
59
61
 
60
62
  class Jogger
61
63
  module NamedTraversals
@@ -63,21 +65,24 @@ No problem. Just define named traversals that aggregate different pipes and give
63
65
  # Traverse to somebody's woman friends
64
66
  def self.friends(current_traversal, gender)
65
67
  t = current_traversal.in(:friends)
66
- t = t.filter{|v| v.properties['gender'] == gender}
68
+ t = t.filter(gender: gender)
67
69
  end
68
70
 
69
71
  # Group and sort
70
72
  def self.top_list(current_traversal, type)
71
73
  t = current_traversal.out(type)
72
- t = t.filter{ |v| v.properties['type'] == 'Movie' }
74
+ t = t.filter(type: 'Movie')
73
75
  t = t.group_count{ |v| v }
74
76
  end
75
77
  end
76
78
  end
77
79
 
80
+ Your methods have to be able to take at least one parameter: the current traversal. It represents the current traversal state of your `Jogger` instance. Your traversal can then modify this traversal and must return it. It will become your Jogger instance's new state.
81
+
78
82
  These are silly examples, but if you look at your traversals I guarantee that you will find repeated patterns all over the place, and Jogger can help you stop repeating these and making the actual traversals much easier on the eyes.
79
83
 
80
- # Installation
84
+ Installation
85
+ ============
81
86
 
82
87
  First, you need to load pacer and whatever graph db connector you need (we use neo4j, by the way) and define your named traversals as above. Jogger doesn't include these on purpose. Then, you have to
83
88
 
@@ -93,10 +98,64 @@ or for your Gemfile
93
98
 
94
99
  That's it!
95
100
 
96
- # Documentation
101
+ Documentation
102
+ =============
97
103
 
98
104
  I gave YARD a shot, so to open the documentation in your browser just do this in the jogger directory:
99
105
 
100
106
  yard server & sleep 3 && open http://localhost:8808/docs/file/README.markdown
101
107
 
102
- Or you can (browse the documentation online)[http://rubydoc.info/github/jayniz/jogger/master/frames]
108
+ Or you can [browse the documentation online](http://rubydoc.info/github/jayniz/jogger/master/frames)
109
+
110
+ Named traversals - The pacer way
111
+ ================================
112
+
113
+ You can implement feature #2 purely in pacer, if you like. For example, you could express
114
+
115
+ my_node.out.filter(type: 'Movie')
116
+
117
+ with
118
+
119
+ my_node.out(Movie)
120
+
121
+ For this to work you need to tell pacer, what `Movie` actually means. In the simplest form, you could say:
122
+
123
+ module Movie
124
+ def self.route_conditions
125
+ { type: 'Movie' }
126
+ end
127
+ end
128
+
129
+ That way pacer knows, whenever you're talking about a `Movie` you want to filter those elements whose `type==Movie`. To do something like the named traversals described above, you could go ahead and define a route to all female likers of a movie:
130
+
131
+ module Girl
132
+ def self.route_conditions
133
+ {gender: :female}
134
+ end
135
+ end
136
+
137
+ module Movie
138
+ def self.route_conditions
139
+ { type: 'Movie' }
140
+ end
141
+
142
+
143
+ module Route
144
+ def female_likers
145
+ self.in_e(:likes).in_v(Girl)
146
+ end
147
+ end
148
+ end
149
+
150
+ You can then go ahead and fetch:
151
+
152
+ t.out(Movie).female_likers
153
+
154
+ To wrap it up, using Jogger to do named traversals is for the super lazy. If you use pacer exclusively and have more complicated structures it would probably make more sense to create your named traversals in the design of your domain logic and do it purely with pacer. You can still use Jogger to traverse these routes. If you want to share common traversal patterns between different models it might be easier to do with Jogger.
155
+
156
+ License
157
+ =======
158
+
159
+ Jogger is released under the MIT license:
160
+
161
+ * http://www.opensource.org/licenses/MIT
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.2
1
+ 0.0.3
@@ -1,3 +1,4 @@
1
+ class UnknownTraversalError < ArgumentError; end;
1
2
 
2
3
  # Allows to formulate traversals by using predefined
3
4
  # named traversals. Also allows for method chaining.
@@ -48,12 +49,12 @@ class Jogger
48
49
  def method_missing(method, *args, &block)
49
50
  begin
50
51
  traversal_args = [method, args].flatten.compact
51
- @current_traversal = Jogger.traverse(@current_traversal, *traversal_args)
52
- rescue ArgumentError
52
+ @current_traversal = Jogger.traverse(@current_traversal, method, args)
53
+ rescue UnknownTraversalError => a
53
54
  begin
54
55
  @current_traversal = @current_traversal.send(method, *args, &block)
55
- rescue NoMethodError
56
- raise "Unknown traversal #{method}"
56
+ rescue NoMethodError => m
57
+ raise "Unknown traversal #{method}. From (#{a}) via (#{m}) (method_missing rocks)"
57
58
  end
58
59
  end
59
60
  self
@@ -70,8 +71,8 @@ class Jogger
70
71
  # @param opts [Object] Whatever you want to pass to the named traverser
71
72
  # @return [Object] The result of the traversal.
72
73
  def self.traverse(traversal_base, named_traversal, opts = nil)
73
- raise ArgumentError, "Unknown traversal #{named_traversal}" unless valid_traversal?(named_traversal)
74
- args = [named_traversal, traversal_base] + [opts].compact
74
+ raise UnknownTraversalError, "Unknown traversal #{named_traversal}" unless valid_traversal?(named_traversal)
75
+ args = [named_traversal, traversal_base] + [*opts].compact
75
76
  Jogger::NamedTraversals.send(*args)
76
77
  end
77
78
 
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "pacer-jogger"
8
- s.version = "0.0.2"
8
+ s.version = "0.0.3"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Jannis Hermanns"]
12
- s.date = "2012-01-25"
12
+ s.date = "2012-09-14"
13
13
  s.description = "Allows to group traversal fragments/pipes to named traversals and call them like they were pacer pipes."
14
14
  s.email = "jannis@gmail.com"
15
15
  s.extra_rdoc_files = [
@@ -18,6 +18,7 @@ Gem::Specification.new do |s|
18
18
  ]
19
19
  s.files = [
20
20
  ".document",
21
+ "CHANGELOG.markdown",
21
22
  "Gemfile",
22
23
  "Gemfile.lock",
23
24
  "LICENSE.txt",
@@ -32,7 +33,7 @@ Gem::Specification.new do |s|
32
33
  s.homepage = "http://github.com/jayniz/jogger"
33
34
  s.licenses = ["MIT"]
34
35
  s.require_paths = ["lib"]
35
- s.rubygems_version = "1.8.10"
36
+ s.rubygems_version = "1.8.24"
36
37
  s.summary = "Pacer traversals for lazy people"
37
38
 
38
39
  if s.respond_to? :specification_version then
@@ -15,10 +15,16 @@ class Jogger
15
15
  (current_traversal || 1) * x
16
16
  end
17
17
 
18
+ # A traversal with no args
18
19
  def self.no_argument_traverser(current_traversal)
19
20
  :worked
20
21
  end
21
22
 
23
+ # A traversal with two args
24
+ def self.two_argument_traverser(current_traversal, x, y)
25
+ x+y
26
+ end
27
+
22
28
  def self.method_missing_dummy(current_traversal)
23
29
  MyFakedPacerResult.new
24
30
  end
@@ -45,13 +51,19 @@ describe Jogger do
45
51
  lambda do
46
52
  p = Jogger.new
47
53
  p.this_does_not_exist
48
- end.should raise_error("Unknown traversal this_does_not_exist")
54
+ end.should raise_error("Unknown traversal this_does_not_exist. From (Unknown traversal this_does_not_exist) via (undefined method `this_does_not_exist' for nil:NilClass) (method_missing rocks)")
49
55
  end
50
56
 
51
57
  it "works for traversals without arguments" do
52
58
  Jogger.new.no_argument_traverser.result.should == :worked
53
59
  end
54
60
 
61
+ it "works for traversals with two arguments" do
62
+ p = Jogger.new
63
+ p.two_argument_traverser(3,7)
64
+ p.result.should == 10
65
+ end
66
+
55
67
  it "delegates method calls to the current traversal" do
56
68
  Jogger.new.method_missing_dummy.some_existing_pacer_method.result.should == :is_there
57
69
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pacer-jogger
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-01-25 00:00:00.000000000Z
12
+ date: 2012-09-14 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: shoulda
16
- requirement: &70106294753680 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,15 @@ dependencies:
21
21
  version: '0'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *70106294753680
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
25
30
  - !ruby/object:Gem::Dependency
26
31
  name: bundler
27
- requirement: &70106294753160 !ruby/object:Gem::Requirement
32
+ requirement: !ruby/object:Gem::Requirement
28
33
  none: false
29
34
  requirements:
30
35
  - - ~>
@@ -32,10 +37,15 @@ dependencies:
32
37
  version: 1.0.0
33
38
  type: :development
34
39
  prerelease: false
35
- version_requirements: *70106294753160
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 1.0.0
36
46
  - !ruby/object:Gem::Dependency
37
47
  name: jeweler
38
- requirement: &70106294752640 !ruby/object:Gem::Requirement
48
+ requirement: !ruby/object:Gem::Requirement
39
49
  none: false
40
50
  requirements:
41
51
  - - ~>
@@ -43,10 +53,15 @@ dependencies:
43
53
  version: 1.7.0
44
54
  type: :development
45
55
  prerelease: false
46
- version_requirements: *70106294752640
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 1.7.0
47
62
  - !ruby/object:Gem::Dependency
48
63
  name: rspec
49
- requirement: &70106294752040 !ruby/object:Gem::Requirement
64
+ requirement: !ruby/object:Gem::Requirement
50
65
  none: false
51
66
  requirements:
52
67
  - - ! '>='
@@ -54,10 +69,15 @@ dependencies:
54
69
  version: '0'
55
70
  type: :development
56
71
  prerelease: false
57
- version_requirements: *70106294752040
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
58
78
  - !ruby/object:Gem::Dependency
59
79
  name: yard
60
- requirement: &70106294751440 !ruby/object:Gem::Requirement
80
+ requirement: !ruby/object:Gem::Requirement
61
81
  none: false
62
82
  requirements:
63
83
  - - ! '>='
@@ -65,7 +85,12 @@ dependencies:
65
85
  version: '0'
66
86
  type: :development
67
87
  prerelease: false
68
- version_requirements: *70106294751440
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
69
94
  description: Allows to group traversal fragments/pipes to named traversals and call
70
95
  them like they were pacer pipes.
71
96
  email: jannis@gmail.com
@@ -76,6 +101,7 @@ extra_rdoc_files:
76
101
  - README.markdown
77
102
  files:
78
103
  - .document
104
+ - CHANGELOG.markdown
79
105
  - Gemfile
80
106
  - Gemfile.lock
81
107
  - LICENSE.txt
@@ -101,7 +127,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
101
127
  version: '0'
102
128
  segments:
103
129
  - 0
104
- hash: 3011242392760856399
130
+ hash: -3862290704607932107
105
131
  required_rubygems_version: !ruby/object:Gem::Requirement
106
132
  none: false
107
133
  requirements:
@@ -110,7 +136,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
110
136
  version: '0'
111
137
  requirements: []
112
138
  rubyforge_project:
113
- rubygems_version: 1.8.10
139
+ rubygems_version: 1.8.24
114
140
  signing_key:
115
141
  specification_version: 3
116
142
  summary: Pacer traversals for lazy people