bud 0.9.7 → 0.9.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,15 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 3d9ec3dd7f92dded5c77e541617853e0954fccaf
4
- data.tar.gz: 37c31e760cbfe5305ebb73d3ff6e2417e499022d
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ OTgyYTM2NWZkYmJlNjI4ODhkNWE3MGMwYmM0ZTFkYjFjMjQxYzdhOA==
5
+ data.tar.gz: !binary |-
6
+ ZDI1ZDMzNWJiYTgzOGFhOTYxMmEyMjM4NjdmZDM5ZjFhYTBkYjg0Zg==
5
7
  SHA512:
6
- metadata.gz: a9fe2678a4aa143ebfa6ae677426bc60593550bda6209edc056855a6bca1b4f7ee5b4fc99e773d74679ca57fc615da7128e6db4622c855d45fda60e138bb78de
7
- data.tar.gz: 240d532afbe76c4a7740f65276e7d0bb405b52661e8dde63fc42e54a5d40deb6d23c0bea1ef7d4a9e0e0e060c837533a0e3202c3ab41f33779861df6db673509
8
+ metadata.gz: !binary |-
9
+ MTlhYWYxMTAyZDVhZWU4OTRmYjIwYjU5NzRlYzY1NDdmYzk3MWJjYTY1MDMw
10
+ OGRlYjNlMzc4MDZkOWZmOTdhZjRmNDYwMmYyYzk1MDU2NmUyNjdkMGU5NjBj
11
+ ZjY4NzYwYjU3MzBlOTlmZjUyMTgwMGEyYjdmNmNlZGMwNmU4MTU=
12
+ data.tar.gz: !binary |-
13
+ MmJkNjZhNzNlMzdhOGJjZDk0ZWRiMWY2MTk2MWY4OWQ1MGI2NzA2NTMxZDk0
14
+ NTdjODdiMDg1MGY3ZTQ3YTBjZmU2ODE4NjYwMTY0NzRiNmRlMDI2YzYzZmZh
15
+ MGVkZWVmZDk5NDllMDFhYTMyMTg3YTUzZGE0NWVhYzgxNGVjYjc=
@@ -1,3 +1,21 @@
1
+ == 0.9.8 / ???
2
+
3
+ * Fix bug in the stratification algorithm
4
+ * Fix bug with insertions into stdio inside bootstrap blocks
5
+ * Improve error message when <~ applied to a collection type that doesn't
6
+ support it (#316)
7
+ * Fix rescan logic for Zookeeper-backed collections (#317)
8
+ * Fix rescan logic for chained negation operators in a single rule
9
+ * Support + operator for concatenating tuples
10
+ * More consistent behavior for #==, #eql?, and #hash methods on Bud tuple
11
+ objects (TupleStruct)
12
+ * Fix intermittent EM::ConnectionNotBound exceptions during the Bud shutdown
13
+ sequence
14
+ * Improve stratification algorithm to be slightly more aggressive; that is, we
15
+ can place certain rules in an earlier strata than we were previously
16
+ (partially fixes #277)
17
+ * Add Rakefile to simplify common development operations (Joel VanderWerf, #324)
18
+
1
19
  == 0.9.7 / 2013-04-22
2
20
 
3
21
  * Avoid raising an exception when Bud is used with Ruby 2.0. There isn't
@@ -0,0 +1,91 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+
4
+ PRJ = "bud"
5
+
6
+ def version
7
+ @version ||= begin
8
+ $LOAD_PATH.unshift 'lib'
9
+ require 'bud/version'
10
+ warn "Bud::VERSION not a string" unless Bud::VERSION.kind_of? String
11
+ Bud::VERSION
12
+ end
13
+ end
14
+
15
+ def tag
16
+ @tag ||= "v#{version}"
17
+ end
18
+
19
+ desc "Run all tests"
20
+ task :test => "test:unit"
21
+
22
+ TESTS = FileList["test/tc_*.rb"]
23
+ SLOW_TESTS = %w{ test/tc_execmodes.rb }
24
+
25
+ namespace :test do
26
+ desc "Run unit tests"
27
+ Rake::TestTask.new :unit do |t|
28
+ t.libs << "lib"
29
+ t.ruby_opts = %w{ -C test }
30
+ t.test_files = TESTS.sub('test/', '')
31
+ ### it would be better to make each tc_*.rb not depend on pwd
32
+ end
33
+
34
+ desc "Run quick unit tests"
35
+ Rake::TestTask.new :quick do |t|
36
+ t.libs << "lib"
37
+ t.ruby_opts = %w{ -C test }
38
+ t.test_files = TESTS.exclude(*SLOW_TESTS).sub('test/', '')
39
+ end
40
+
41
+ desc "Run quick non-zk unit tests"
42
+ Rake::TestTask.new :quick_no_zk do |t|
43
+ t.libs << "lib"
44
+ t.ruby_opts = %w{ -C test }
45
+ t.test_files = TESTS.
46
+ exclude('test/tc_zookeeper.rb').
47
+ exclude(*SLOW_TESTS).
48
+ sub('test/', '')
49
+ end
50
+ end
51
+
52
+ desc "Commit, tag, and push repo; build and push gem"
53
+ task :release => "release:is_new_version" do
54
+ require 'tempfile'
55
+
56
+ sh "gem build #{PRJ}.gemspec"
57
+
58
+ file = Tempfile.new "template"
59
+ begin
60
+ file.puts "release #{version}"
61
+ file.close
62
+ sh "git commit --allow-empty -a -v -t #{file.path}"
63
+ ensure
64
+ file.close unless file.closed?
65
+ file.unlink
66
+ end
67
+
68
+ sh "git tag #{tag}"
69
+ sh "git push"
70
+ sh "git push --tags"
71
+
72
+ sh "gem push #{tag}.gem"
73
+ end
74
+
75
+ namespace :release do
76
+ desc "Diff to latest release"
77
+ task :diff do
78
+ latest = `git describe --abbrev=0 --tags --match 'v*'`.chomp
79
+ sh "git diff #{latest}"
80
+ end
81
+
82
+ desc "Log to latest release"
83
+ task :log do
84
+ latest = `git describe --abbrev=0 --tags --match 'v*'`.chomp
85
+ sh "git log #{latest}.."
86
+ end
87
+
88
+ task :is_new_version do
89
+ abort "#{tag} exists; update version!" unless `git tag -l #{tag}`.empty?
90
+ end
91
+ end
@@ -21,6 +21,10 @@ def make_instance(mods)
21
21
  # If we're given a single identifier that names a class, just return an
22
22
  # instance of that class. Otherwise, define a bogus class that includes all
23
23
  # the module names specified by the user and return an instance.
24
+ tmpserver = TCPServer.new('127.0.0.1', 0) # get a free port
25
+ default_params = {:dbm_dir => "/tmp/budplot_dbm_" + SecureRandom.uuid.to_s, :port => tmpserver.addr[1]}
26
+
27
+
24
28
  mods.each do |m|
25
29
  unless is_constant? m
26
30
  puts "Error: unable to find definition for module or class \"#{m}\""
@@ -30,7 +34,7 @@ def make_instance(mods)
30
34
  mod_klass = eval m
31
35
  if mod_klass.class == Class
32
36
  if mods.length == 1
33
- return mod_klass.new
37
+ return mod_klass.new(default_params)
34
38
  else
35
39
  puts "Error: cannot intermix class \"#{mod_klass}\" with modules"
36
40
  exit
@@ -50,7 +54,7 @@ def make_instance(mods)
50
54
  ]
51
55
  class_def = def_lines.flatten.join("\n")
52
56
  eval(class_def)
53
- f =FooBar.new
57
+ f = FooBar.new(default_params)
54
58
  3.times{ f.tick }
55
59
  f
56
60
  end
@@ -220,6 +224,7 @@ end
220
224
  modules = []
221
225
  ARGV.each do |arg|
222
226
  if File.exists? arg
227
+ arg = File.expand_path arg
223
228
  require arg
224
229
  else
225
230
  modules << arg
@@ -6,28 +6,19 @@ Welcome to the documentation for *Bud*, a prototype of Bloom under development.
6
6
  The documents here are organized to be read in any order, but you might like to
7
7
  try the following:
8
8
 
9
- * [intro.md][intro]: A brief introduction to Bud and Bloom.
10
- * [getstarted.md][getstarted]: A quickstart to teach you basic Bloom
9
+ * [intro](intro.md): A brief introduction to Bud and Bloom.
10
+ * [getstarted](getstarted.md): A quickstart to teach you basic Bloom
11
11
  concepts, the use of `rebl` interactive terminal, and the embedding of Bloom
12
12
  code in Ruby via the `Bud` module.
13
- * [operational.md][operational]: An operational view of Bloom, to provide
13
+ * [operational](operational.md): An operational view of Bloom, to provide
14
14
  a more detailed model of how Bloom code is evaluated by Bud.
15
- * [cheat.md][cheat]: A concise "cheat sheet" to remind you about Bloom syntax.
16
- * [modules.md][modules]: An overview of Bloom's modularity features.
17
- * [ruby\_hooks.md][ruby_hooks]: Bud module methods that allow you to
15
+ * [cheat](cheat.md): Full documentation of the language constructs in a concise "cheat sheet" style.
16
+ * [modules](modules.md): An overview of Bloom's modularity features.
17
+ * [ruby_hooks](ruby\_hooks.md): Bud module methods that allow you to
18
18
  interact with the Bud evaluator from other Ruby threads.
19
- * [visualizations.md][visualizations]: Overview of the `budvis` and
19
+ * [visualizations](visualizations.md): Overview of the `budvis` and
20
20
  `budplot` tools for visualizing Bloom program analyses.
21
- * [bfs.md][bfs]: A walkthrough of the Bloom distributed filesystem.
22
-
23
- [intro]: /docs/intro.md
24
- [getstarted]: /docs/getstarted.md
25
- [operational]: /docs/operational.md
26
- [cheat]: /docs/cheat.md
27
- [modules]: /docs/modules.md
28
- [ruby_hooks]: /docs/ruby_hooks.md
29
- [visualizations]: /docs/visualizations.md
30
- [bfs]: /docs/bfs.md
21
+ * [bfs](bfs.md): A walkthrough of the Bloom distributed filesystem.
31
22
 
32
23
  In addition, the [bud-sandbox](http://github.com/bloom-lang/bud-sandbox) GitHub
33
24
  repository contains lots of useful libraries and example programs built using
@@ -194,7 +194,7 @@ implicit map:
194
194
  end
195
195
 
196
196
  ## BudCollection-Specific Methods ##
197
- `bc.schema`: returns the schema of `bc` (Hash of key column names => non-key column names). Note that for channels, this omits the location specifier (<tt>@</tt>).<br>
197
+ `bc.schema`: returns the schema of `bc` (Hash of key column names => non-key column names; if no non-key columns, just an Array of key column names). Note that for channels, this omits the location specifier (<tt>@</tt>).<br>
198
198
 
199
199
  `bc.cols`: returns the column names in `bc` as an Array<br>
200
200
 
@@ -66,9 +66,11 @@ Second, note that our Bud program's one statement merges the values on its right
66
66
  ### Tables and Scratches ###
67
67
  Before we dive into writing server code, let's try a slightly more involved single-timestep example. Start up rebl again, and paste in the following:
68
68
 
69
- table :clouds
70
- clouds <= [[1, "Cirrus"], [2, "Cumulus"]]
71
- stdio <~ clouds.inspected
69
+ ``` ruby
70
+ table :clouds
71
+ clouds <= [[1, "Cirrus"], [2, "Cumulus"]]
72
+ stdio <~ clouds.inspected
73
+ ```
72
74
 
73
75
  Now tick your rebl, but don't quit yet.
74
76
 
@@ -128,8 +130,10 @@ Now that we've seen a bit of Bloom, we're ready to write our first interesting s
128
130
 
129
131
  Even though we're getting ahead of ourselves, let's have a peek at the Bloom statements that implement the server in `examples/chat/chat_server.rb`:
130
132
 
131
- nodelist <= connect { |c| [c.client, c.nick] }
132
- mcast <~ (mcast * nodelist).pairs { |m,n| [n.key, m.val] }
133
+ ``` ruby
134
+ nodelist <= connect { |c| [c.client, c.nick] }
135
+ mcast <~ (mcast * nodelist).pairs { |m,n| [n.key, m.val] }
136
+ ```
133
137
 
134
138
  That's it! There is one statement for each of the two sentences describing the behavior of the "basic idea" above. We'll go through these two statements in more detail shortly. But it's nice to see right away how concisely and naturally a Bloom program can fit our intuitive description of a distributed service.
135
139
 
@@ -137,14 +141,16 @@ That's it! There is one statement for each of the two sentences describing the
137
141
 
138
142
  Now that we've satisfied our need to peek, let's take this a bit more methodically. First we need declarations for the various Bloom collections we'll be using. We put the declarations that are common to both client and server into file `examples/chat/chat_protocol.rb`:
139
143
 
140
- module ChatProtocol
141
- state do
142
- channel :connect, [:@addr, :client] => [:nick]
143
- channel :mcast
144
- end
145
-
146
- DEFAULT_ADDR = "localhost:12345"
147
- end
144
+ ``` ruby
145
+ module ChatProtocol
146
+ state do
147
+ channel :connect, [:@addr, :client] => [:nick]
148
+ channel :mcast
149
+ end
150
+
151
+ DEFAULT_ADDR = "localhost:12345"
152
+ end
153
+ ```
148
154
 
149
155
  This defines a [Ruby mixin module](http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_modules.html) called `ChatProtocol` that has a couple special Bloom features:
150
156
 
@@ -156,33 +162,34 @@ This defines a [Ruby mixin module](http://www.ruby-doc.org/docs/ProgrammingRuby/
156
162
 
157
163
  Given this protocol (and the Ruby constant at the bottom), we're now ready to examine `examples/chat/chat_server.rb` in more detail:
158
164
 
159
- require 'rubygems'
160
- require 'bud'
161
- require 'chat_protocol'
162
-
163
- class ChatServer
164
- include Bud
165
- include ChatProtocol
166
-
167
- state { table :nodelist }
165
+ ``` ruby
166
+ require 'rubygems'
167
+ require 'bud'
168
+ require_relative 'chat_protocol'
168
169
 
169
- bloom do
170
- nodelist <= connect { |c| [c.client, c.nick] }
171
- mcast <~ (mcast * nodelist).pairs { |m,n| [n.key, m.val] }
172
- end
173
- end
174
-
175
- if ARGV.first
176
- addr = ARGV.first
177
- else
178
- addr = ChatProtocol::DEFAULT_ADDR
179
- end
170
+ class ChatServer
171
+ include Bud
172
+ include ChatProtocol
180
173
 
181
- ip, port = addr.split(":")
182
- puts "Server address: #{ip}:#{port}"
183
- program = ChatServer.new(:ip => ip, :port => port.to_i)
184
- program.run
174
+ state { table :nodelist }
185
175
 
176
+ bloom do
177
+ nodelist <= connect { |c| [c.client, c.nick] }
178
+ mcast <~ (mcast * nodelist).pairs { |m,n| [n.key, m.val] }
179
+ end
180
+ end
181
+
182
+ if ARGV.first
183
+ addr = ARGV.first
184
+ else
185
+ addr = ChatProtocol::DEFAULT_ADDR
186
+ end
187
+
188
+ ip, port = addr.split(":")
189
+ puts "Server address: #{ip}:#{port}"
190
+ program = ChatServer.new(:ip => ip, :port => port.to_i)
191
+ program.run_fg
192
+ ```
186
193
 
187
194
  The first few lines get the appropriate Ruby classes and modules loaded via `require`. We then define the ChatServer class which mixes in the `Bud` module and the ChatProtocol module we looked at above. Then we have another `state` block that declares one additional collection, the `nodelist` table.
188
195
 
@@ -190,14 +197,18 @@ With those preliminaries aside, we have our first `bloom` block, which is how Bl
190
197
 
191
198
  The first is pretty simple:
192
199
 
193
- nodelist <= connect { |c| [c.client, c.nick] }
200
+ ``` ruby
201
+ nodelist <= connect { |c| [c.client, c.nick] }
202
+ ```
194
203
 
195
204
  This says that whenever messages arrive on the channel named "connect", the client address and user-provided nickname should be instantaneously merged into the table "nodelist", which will store them persistently. Note that nodelist has a \[key/val\] pair structure, so it is suitable for storing pairs of (IP address, nickname).
196
205
 
197
206
  The next Bloom statement is more complex. Remember the description in the "basic idea" at the beginning of this section: the server needs to accept inbound chat messages from clients and forward them to other clients.
198
207
 
199
- mcast <~ (mcast * nodelist).pairs { |m,n| [n.key, m.val] }
200
-
208
+ ``` ruby
209
+ mcast <~ (mcast * nodelist).pairs { |m,n| [n.key, m.val] }
210
+ ```
211
+
201
212
  The first thing to note is the lhs and operator in this statement. We are merging items (asynchronously, of course!) into the mcast channel, where they will be sent to their eventual destination.
202
213
 
203
214
  The rhs is our first introduction to the `*` operator of Bloom collections, and the `pairs` method after it. You can think of the `*` operator as "all-pairs": it produces a Bloom collection containing all pairs of mcast and nodelist items. The `pairs` method iterates through these pairs, passing them through a code block via the block arguments `m` and `n`. Finally, for each such pair the block produces an item containing the `key` attribute of the nodelist item, and the `val` attribute of the mcast item. This is structured as a proper \[address, val\] entry to be merged back into the mcast channel. Putting this together, this statement *multicasts inbound payloads on the mcast channel to all nodes in the chat*.
@@ -205,7 +216,7 @@ The rhs is our first introduction to the `*` operator of Bloom collections, and
205
216
  The remaining lines of plain Ruby simply instantiate and run the ChatServer class (which includes the `Bud` module) using an ip and port given on the command line (or the default from ChatProtocol.rb).
206
217
 
207
218
  #### `*`'s and Clouds ####
208
- You can think of out use of the `*` operator in the rhs of the second statement in a few different ways:
219
+ You can think of our use of the `*` operator on the rhs of the second statement in a few different ways:
209
220
 
210
221
  * If you're familiar with event-loop programming, this implements an *event handler* for messages on the mcast channel: whenever an mcast message arrives, this handler performs lookups in the nodelist table to form new messages. (It is easy to add "filters" to these handlers as arguments to `pairs`.) The resulting messages are dispatched via the mcast channel accordingly. This is a very common pattern in Bloom programs: handling channel messages via lookups in a table.
211
222
 
@@ -218,49 +229,51 @@ Given our understanding of the server, the client should be pretty simple. It n
218
229
 
219
230
  And here's the code:
220
231
 
221
- require 'rubygems'
222
- require 'bud'
223
- require 'chat_protocol'
224
-
225
- class ChatClient
226
- include Bud
227
- include ChatProtocol
228
-
229
- def initialize(nick, server, opts={})
230
- @nick = nick
231
- @server = server
232
- super opts
233
- end
234
-
235
- bootstrap do
236
- connect <~ [[@server, ip_port, @nick]]
237
- end
238
-
239
- bloom do
240
- mcast <~ stdio do |s|
241
- [@server, [ip_port, @nick, Time.new.strftime("%I:%M.%S"), s.line]]
242
- end
243
-
244
- stdio <~ mcast { |m| [pretty_print(m.val)] }
245
- end
246
-
247
- # format chat messages with timestamp on the right of the screen
248
- def pretty_print(val)
249
- str = val[1].to_s + ": " + (val[3].to_s || '')
250
- pad = "(" + val[2].to_s + ")"
251
- return str + " "*[66 - str.length,2].max + pad
252
- end
253
- end
232
+ ``` ruby
233
+ require 'rubygems'
234
+ require 'bud'
235
+ require_relative 'chat_protocol'
236
+
237
+ class ChatClient
238
+ include Bud
239
+ include ChatProtocol
254
240
 
255
- if ARGV.length == 2
256
- server = ARGV[1]
257
- else
258
- server = ChatProtocol::DEFAULT_ADDR
241
+ def initialize(nick, server, opts={})
242
+ @nick = nick
243
+ @server = server
244
+ super opts
245
+ end
246
+
247
+ bootstrap do
248
+ connect <~ [[@server, ip_port, @nick]]
249
+ end
250
+
251
+ bloom do
252
+ mcast <~ stdio do |s|
253
+ [@server, [ip_port, @nick, Time.new.strftime("%I:%M.%S"), s.line]]
259
254
  end
260
255
 
261
- puts "Server address: #{server}"
262
- program = ChatClient.new(ARGV[0], server, :read_stdin => true)
263
- program.run_fg
256
+ stdio <~ mcast { |m| [pretty_print(m.val)] }
257
+ end
258
+
259
+ # format chat messages with timestamp on the right of the screen
260
+ def pretty_print(val)
261
+ str = val[1].to_s + ": " + (val[3].to_s || '')
262
+ pad = "(" + val[2].to_s + ")"
263
+ return str + " "*[66 - str.length,2].max + pad
264
+ end
265
+ end
266
+
267
+ if ARGV.length == 2
268
+ server = ARGV[1]
269
+ else
270
+ server = ChatProtocol::DEFAULT_ADDR
271
+ end
272
+
273
+ puts "Server address: #{server}"
274
+ program = ChatClient.new(ARGV[0], server, :stdin => $stdin)
275
+ program.run_fg
276
+ ```
264
277
 
265
278
  The ChatClient class has a typical Ruby `initialize` method that sets up two local instance variables: one for this client's nickname, and another for the 'IP:port' address string for the server. It then calls the initializer of the Bud superclass passing along a hash of options.
266
279
 
@@ -293,4 +306,4 @@ In this section we saw a number of features that we missed in our earlier single
293
306
  * **the * operator and pairs method**: the way to combine items from multiple collections.
294
307
 
295
308
  # The Big Picture and the Details #
296
- Now that you've seen some working Bloom code, hopefully you're ready to delve deeper. The [README](README.md) provides links to places you can go for more information. Have fun and [stay in touch](http://groups.google.com/group/bloom-lang)!
309
+ Now that you've seen some working Bloom code, hopefully you're ready to delve deeper. The [README](README.md) provides links to places you can go for more information. Have fun and [stay in touch](http://groups.google.com/group/bloom-lang)!