Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

more work

  • Loading branch information...
commit deeb29d3676653b39107090f634e364bfb26f235 1 parent d9e43ce
@daveray authored
View
76 README.md
@@ -0,0 +1,76 @@
+Want to use Clojure's persistent, lazy data structures and concurrency primitives, but afraid of parentheses? Try it from Ruby.
+
+Use the Clojure runtime from Ruby, JRuby in particular.
+
+# Usage
+
+ require 'familiar'
+
+Functions in `clojure.core` are mapped to the `Familiar` module, replacing hyphens with underscores:
+
+ (clojure.core/hash-map "a" 1 "b" 2)
+
+becomes:
+
+ Familiar.hash_map "a", 1, "b" 2
+
+Some functions have additional Ruby sugar. See below.
+
+# Persistent data structures
+
+
+ # Create a vector
+ v = Familiar.vector 1, 2, 3, 4
+ v.nth 2
+ w = v.assoc 2 "hi"
+
+ # Create a map
+ v = Familiar.hash_map "a", 1, "b", 2
+ w = v.assoc "c", 3
+ w["c"] -> 3
+
+# Atoms
+
+ a = Familiar.atom(99)
+ a.swap! |v|
+ v + 1
+ end
+ a.deref -> 100
+ a.reset! 101
+ a.deref -> 101
+
+# Refs and STM
+
+ r = Familiar.ref(99)
+ Familiar.dosync do
+ r.alter {|v| v + 1}
+ end
+ r.deref -> 100
+
+# Agents
+
+ a = Familiar.agent(99)
+ a.send_ do |v|
+ java.lang.Thread.sleep 10000
+ v + 1
+ end
+ a.deref -> 99
+ # ... 10 seconds later ...
+ a.deref -> 100
+
+Note that it's `send_`, not `send` since that conflicts with the built-in Ruby method with that name.
+
+# Sequences
+
+Here's how you can make a lazy sequence
+
+ def my_range(n)
+ Familiar.lazy_seq do
+ if n == 0
+ nil
+ else
+ Familiar.cons n, my_range(n - 1)
+ end
+ end
+ end
+
View
9 Rakefile
@@ -0,0 +1,9 @@
+require 'rake/testtask'
+
+Rake::TestTask.new do |t|
+ t.libs << 'test'
+end
+
+desc "Run tests"
+task :default => :test
+
View
5 familiar.gemspec
@@ -1,3 +1,4 @@
+# $ jgem build familiar.gemspec && jgem install ./familiar-0.0.0.gem
Gem::Specification.new do |s|
s.name = 'familiar'
s.version = '0.0.0'
@@ -7,10 +8,6 @@ Gem::Specification.new do |s|
s.authors = ["Dave Ray"]
s.email = 'daveray@gmail.com'
s.files = ["lib/familiar.rb",
- "lib/familiar/fn.rb",
- "lib/familiar/atom.rb",
- "lib/familiar/ref.rb",
- "lib/familiar/seq.rb",
"lib/clojure-1.3.0.jar"]
s.homepage = 'http://rubygems.org/gems/familiar'
end
View
112 lib/familiar.rb
@@ -1,32 +1,112 @@
require "java"
require "clojure-1.3.0.jar"
-require 'familiar/fn'
-require 'familiar/atom'
-require 'familiar/ref'
-require 'familiar/seq'
-
module Familiar
- def self.future(&code)
- Java::clojure.lang.Agent.soloExecutor.submit(Callable.new(code))
+ # map some_func to clojure.core/some-func
+ def self.method_missing(meth, *args, &block)
+ #puts "Missing #{meth}"
+ m = Java::clojure.lang.RT.var("clojure.core", meth.to_s.gsub("_", "-"))
+ if m.is_bound?
+ m.invoke(*args)
+ else
+ super
+ end
+ end
+
+ #############################################################################
+ # Functions
+
+ class Callable
+ include Java::java.util.concurrent.Callable
+ def initialize(callable)
+ @callable = callable
+ end
+
+ def call
+ @callable.call
+ end
end
- def self.print(v)
- puts(Java::clojure.lang.RT.print_string(v))
+ class Fn < Java::clojure.lang.AFn
+ def initialize &block
+ @block = block
+ end
+
+ def invoke(*args)
+ @block.call *args
+ end
end
-
- def self.hash_map(*args)
- Java::clojure.lang.RT.map(*args)
+ def self.fn(p)
+ Fn.new &p
end
- def self.hash_set(*args)
- Java::clojure.lang.RT.set(*args)
+ #############################################################################
+ # Seqs
+
+ def self.lazy_seq(&code)
+ Java::clojure.lang.LazySeq.new(Familiar.fn(code))
end
- def self.vector(*args)
- Java::clojure.lang.RT.vector(*args)
+ #############################################################################
+ # Atoms
+
+ def self.atom?(v)
+ v.is_a? Java::clojure.lang.Atom
+ end
+
+ class Java::ClojureLang::Atom
+ def swap!(&code)
+ swap(Familiar.fn(code))
+ end
+
+ def reset!(v)
+ reset(v)
+ end
+ end
+
+ #############################################################################
+ # Refs and STM
+
+ def self.dosync(&code)
+ Java::clojure.lang.LockingTransaction.runInTransaction(Callable.new(code))
+ end
+
+ class Java::ClojureLang::Ref
+ def alter(&code)
+ java_send :alter,
+ [Java::clojure.lang.IFn.java_class,
+ Java::clojure.lang.ISeq.java_class],
+ Familiar.fn(code),
+ nil
+ end
+
+ def commute(&code)
+ java_send :commute,
+ [Java::clojure.lang.IFn.java_class,
+ Java::clojure.lang.ISeq.java_class],
+ Familiar.fn(code),
+ nil
+ end
+ end
+
+ #############################################################################
+ # Agents
+
+ class Java::ClojureLang::Agent
+ def send_(&code)
+ Familiar.send(self, Familiar.fn(code))
+ end
+
+ def send_off(&code)
+ Familiar.send_off(self, Familiar.fn(code))
+ end
+ end
+
+
+ def self.future(&code)
+ Java::clojure.lang.Agent.soloExecutor.submit(Callable.new(code))
end
end
View
16 lib/familiar/atom.rb
@@ -1,16 +0,0 @@
-module Familiar
-
- def self.atom(v)
- Java::clojure.lang.Atom.new v
- end
-
- class Java::ClojureLang::Atom
- def swap!(&code)
- swap(Familiar.fn(code))
- end
- def reset!(v)
- reset(v)
- end
- end
-
-end
View
28 lib/familiar/fn.rb
@@ -1,28 +0,0 @@
-module Familiar
-
- class Callable
- include Java::java.util.concurrent.Callable
- def initialize(callable)
- @callable = callable
- end
-
- def call
- @callable.call
- end
- end
-
- class Fn < Java::clojure.lang.AFn
- def initialize &block
- @block = block
- end
-
- def invoke(*args)
- @block.call *args
- end
- end
-
- def self.fn(p)
- Fn.new &p
- end
-
-end
View
12 lib/familiar/ref.rb
@@ -1,12 +0,0 @@
-module Familiar
-
- def self.ref(v)
- Java::clojure.lang.Ref.new v
- end
-
- def self.dosync(&code)
- Java::clojure.lang.LockingTransaction.runInTransaction(Callable.new(code))
- end
-
-
-end
View
16 lib/familiar/seq.rb
@@ -1,16 +0,0 @@
-module Familiar
-
- def self.seq(coll)
- Java::clojure.lang.RT.seq(coll)
- end
- def self.first(coll)
- Java::clojure.lang.RT.first(coll)
- end
- def self.rest(coll)
- Java::clojure.lang.RT.more(coll)
- end
- def self.next(coll)
- Java::clojure.lang.RT.next(coll)
- end
-
-end
View
34 test.rb
@@ -0,0 +1,34 @@
+require 'rubygems'
+require 'familiar'
+
+clj = Familiar
+clj.fn(lambda {|x,y| puts "HERE!!! #{x + y}"}).invoke(5, 6)
+f = clj.fn(lambda {|x| x + 1 })
+a = clj.atom(0)
+a.swap(f)
+a.swap! do |v|
+ v + 1
+end
+
+(1..10).each do |i|
+ clj.future do
+ (1..1000).each do |j|
+ a.reset!(a.deref + 1)
+ a.swap! do |v|
+ v + 1
+ end
+ a.swap(f)
+ end
+ puts "#{i} done!"
+ end
+end
+Java::java.lang.Thread.sleep 1000
+clj.print a
+
+y = clj.ref("This is a value")
+clj.dosync do
+ puts "HERE I AM IN DOSYNC"
+ y.set "A new value"
+end
+clj.print y
+
View
106 test/test_familiar.rb
@@ -0,0 +1,106 @@
+require 'test/unit'
+require 'familiar'
+
+class FamiliarAtomTest < Test::Unit::TestCase
+
+ # familiar.rb
+ def test_can_create_a_list
+ input = (1..100).to_a
+ a = Familiar.list(*input)
+ assert a.is_a? Java::clojure.lang.IPersistentList
+ assert_equal input.count, a.count
+ assert_equal 1, a.first
+ end
+
+ def test_can_create_a_vector
+ a = Familiar.vector("a", "b", "c", "d")
+ assert a.is_a? Java::clojure.lang.IPersistentVector
+ assert_equal 4, a.count
+ assert_equal "a", a.nth(0)
+ assert_equal "b", a.nth(1)
+ assert_equal "c", a.nth(2)
+ assert_equal "d", a.nth(3)
+ end
+
+ def test_can_create_a_hash_map
+ a = Familiar.hash_map("a", "b", "c", "d")
+ assert a.is_a? Java::clojure.lang.IPersistentMap
+ assert_equal "b", a["a"]
+ assert_equal "d", a["c"]
+ end
+
+ def test_can_create_a_hash_set
+ a = Familiar.hash_set("a", "b", "c", "d")
+ assert a.is_a? Java::clojure.lang.IPersistentSet
+ assert a.contains?("a")
+ assert a.contains?("b")
+ assert a.contains?("c")
+ assert a.contains?("d")
+ end
+
+ def test_can_conj_on_a_vector
+ v = Familiar.vector(1, 2, 3, 4)
+ w = Familiar.conj v, 5
+ assert_equal Familiar.vector(1, 2, 3, 4), v
+ assert_equal Familiar.vector(1, 2, 3, 4, 5), w
+ end
+
+ # atom.rb
+ def test_atom_creates_an_atom
+ a = Familiar.atom(3)
+ assert a.is_a? Java::clojure.lang.Atom
+ assert_equal 3, a.deref
+ end
+
+ def test_can_swap_the_value_of_an_atom
+ a = Familiar.atom("hello")
+ result = a.swap! {|v| v + " world" }
+ assert_equal "hello world", result
+ assert_equal "hello world", a.deref
+ end
+
+ def test_can_reset_the_value_of_an_atom
+ a = Familiar.atom([1, 3, 4])
+ result = a.reset! "hi"
+ assert_equal "hi", result
+ assert_equal "hi", a.deref
+ end
+
+ def test_atom?
+ assert Familiar.atom?(Familiar.atom("hi"))
+ end
+
+###############################################
+
+ def test_ref_creates_a_ref
+ r = Familiar.ref(Familiar.vector 1, 2, 3, 4)
+ assert r.is_a? Java::clojure.lang.Ref
+ assert_equal Familiar.vector(1, 2, 3, 4), r.deref
+ end
+
+ def test_can_set_a_refs_value
+ r = Familiar.ref(6)
+ Familiar.dosync do
+ r.set 7
+ end
+ assert_equal 7, r.deref
+ end
+
+ def test_can_alter_a_refs_value
+ r = Familiar.ref(10)
+ Familiar.dosync do
+ r.alter {|v| v * 2}
+ end
+ assert_equal 20, r.deref
+ end
+
+ def test_can_commute_a_refs_value
+ r = Familiar.ref(11)
+ Familiar.dosync do
+ r.commute {|v| v * 3}
+ end
+ assert_equal 33, r.deref
+ end
+
+end
+
Please sign in to comment.
Something went wrong with that request. Please try again.