zweikopf 0.0.6 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -17,8 +17,15 @@ Or install it yourself as:
17
17
 
18
18
  $ gem install zweikopf
19
19
 
20
+ For Clojure-driven projects, add this line to your `project.clj`:
21
+
22
+ ```clojure
23
+ [zweikopf "0.4.0"]
24
+ ```
20
25
  ## Usage
21
26
 
27
+ ### From Ruby code: Clojure->Ruby transformations
28
+
22
29
  Transfroming from Clojure entities to Ruby ones is as easy as:
23
30
 
24
31
  ```ruby
@@ -35,6 +42,8 @@ Zweikopf::Transformer.from_clj(clojure_var)
35
42
  # => {:a => 1, :b => {:c => [{:d => 2}, {:e => 3}, { :f => 4}]}}
36
43
  ```
37
44
 
45
+ ### From Ruby code: Ruby->Clojure transformations
46
+
38
47
  And backwards:
39
48
 
40
49
  ```ruby
@@ -51,6 +60,8 @@ Zweikopf::Transformer.from_ruby(ruby_var)
51
60
  # => {:a 1 :b {:c [{:d 2} {:e 3} {:f 4}]}}
52
61
  ```
53
62
 
63
+ ### Custom conversion
64
+
54
65
  When performing Ruby to Clojure transformation, you may leave out some space for customization:
55
66
 
56
67
  ```ruby
@@ -70,8 +81,74 @@ Zweikopf::Transformer.from_ruby({:a => 1, :b => CustomTransformedEntry.new }) do
70
81
  # => {:a 1 :b {:c 3 :d 4}}
71
82
  ```
72
83
 
84
+ ### From Clojure code:
85
+
86
+ With Clojure version everything is extremely simple:
87
+
88
+ ```clojure
89
+ (:require 'zweikopf.core)
90
+
91
+ ;; You _must_ call it, otherwise Ruby Runtime won't get initialized.
92
+ (init-ruby-context)
93
+
94
+ ;; To convert Clojure DS to Ruby, run:
95
+ (rubyize {:a 1 :b 2})
96
+
97
+ ;; To convert Ruby DS to Clojure, run
98
+ (clojurize ruby-obj)
99
+
100
+ ;; If you want to execute arbitrary Ruby code, use ruby-eval:
101
+ (ruby-eval "puts 'Hello World'") ;; Or any other portion of Ruby code you'd like to execute
102
+
103
+ ;; In order to require a file:
104
+ (ruby-require "filename")
105
+
106
+ ;; In order to load:
107
+ (ruby-load "filename")
108
+
109
+ ;; Call a method on a Ruby object:
110
+ ;; This will call `#to_hash` method on `ruby-obj`
111
+ (call-ruby ruby-obj :to_hash)
112
+
113
+ ;; To set gem-path:
114
+ (set-gem-path "my-gem-path")
115
+
116
+ ;; To set gem-home:
117
+ (set-gem-path "my-gem-home")
118
+
119
+ ;; To add custom convertor from Ruby to Clojure, extend protocol Clojurize
120
+ ;; For example, convertion of RubyTime class to java Date
121
+ (extend-protocol Clojurize
122
+ org.jruby.RubyTime
123
+ (clojurize [this]
124
+ (.toJava this java.util.Date)))
125
+
126
+ ;; To add custom convertor from Clojure to Ruby, extend protocol Rubyize
127
+ ;; For example, convertion of Clojure Keyword class to Ruby Symbol
128
+ (extend-protocol Rubyize
129
+ clojure.lang.Keyword
130
+ (rubyize [this]
131
+ (.fastNewSymbol ruby-runtime (name this))))
132
+ ```
133
+
134
+ # Pitfalls
135
+
136
+ When using Rails and DateTime conversion, you should call `DateTime#utc` before you can call `#to_time`.
137
+
138
+ It's very easy to package all your gems in a Jar, if you decide to do so, you need to either use
139
+ the files that were extracted by the runtime (which is by itself quite tricky, and you may run into
140
+ some issues with Bundler, if you use it), alternative is to materialize (extract) your gems from
141
+ jar manually. We're not yet ready to open our sorce for jar extraction, but you can write up your
142
+ own quite quickly, using `FileReader`, `JarFile` and `JarInputStream` files.
143
+
144
+ Other than that, JRuby/Clojure integration is very smooth and painless.
145
+
73
146
  # Performance
74
147
 
148
+ We highly recommend using target language convertor. If you pass rather small data structures to Ruby scripts,
149
+ and return large portions back, use Clojure version. If you pass smaller amounts of data to Clojure code,
150
+ and it returns larger cunks, use Ruby version of transformer.
151
+
75
152
  ## Conversion from Ruby hash to Clojure PersistentHash Map
76
153
 
77
154
  Most of time 52% according to the rough estimate is spent while converting from ruby Symbol to clojure Keyword.
@@ -86,6 +163,10 @@ Most of time 52% according to the rough estimate is spent while converting from
86
163
 
87
164
  # Copyright
88
165
 
89
- Copyright (C) 2011-2012 Alex Petrov, Maximilian Karasz, Daniel Steiner, Roman Flammer.
166
+ Copyright (C) 2012-2013 Alex Petrov and [contributors](https://github.com/ifesdjeen/zweikopf/graphs/contributors).
90
167
 
91
168
  Distributed under the Eclipse Public License, the same as Clojure.
169
+
170
+
171
+ [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/ifesdjeen/zweikopf/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
172
+
@@ -1,3 +1,3 @@
1
1
  module Zweikopf
2
- VERSION = "0.0.6"
2
+ VERSION = "0.4.0"
3
3
  end
data/project.clj CHANGED
@@ -1,5 +1,5 @@
1
- (defproject zweikopf "0.1.0-SNAPSHOT"
1
+ (defproject zweikopf "1.0.0-SNAPSHOT"
2
2
  :description "jruby clojure interop"
3
3
  :dependencies [[org.clojure/clojure "1.4.0"]
4
- [org.jruby/jruby-complete "1.6.7.2"]]
4
+ [org.jruby/jruby-complete "1.7.1"]]
5
5
  :profiles {:dev {:dependencies []}})
@@ -14,8 +14,9 @@ describe :performance do
14
14
 
15
15
  it "this spec simply shows performance, very roughly, please do your testing depending on your needs" do
16
16
  time_before = Time.now.to_f
17
+ ruby_obj = {:a => 1, :b => 2}
17
18
  100000.times do |i|
18
- Zweikopf::Transformer.from_ruby({:a => i, :b => i})
19
+ Zweikopf::Transformer.from_ruby(ruby_obj)
19
20
  end
20
21
  puts "Ruby to Clojure, time elapsed: #{Time.now.to_f - time_before} seconds"
21
22
 
@@ -1,33 +1,45 @@
1
1
  (ns zweikopf.core
2
- (:require [clojure.reflect :as r])
3
- (:import [org.jruby RubyObject RubyHash RubyBasicObject
4
- RubySymbol RubyHash$RubyHashEntry RubyArray]
5
- [org.jruby.javasupport JavaUtil]))
6
-
7
- (defprotocol Clojurize
8
- (clojurize [this]))
9
-
10
- (extend-protocol Clojurize
11
- java.lang.Object
12
- (clojurize [this] this)
13
- RubyObject
14
- (clojurize [this] (JavaUtil/convertRubyToJava this))
15
- RubySymbol
16
- (clojurize [^RubySymbol this]
17
- (clojure.lang.Keyword/intern (.toString this)))
18
- RubyHash
19
- (clojurize [^RubyHash this]
20
- (loop [[^RubyHash$RubyHashEntry entry & entries] (seq (.directEntrySet this))
21
- acc (transient {})]
22
- (if entry
23
- (recur entries
24
- (assoc! acc (clojurize (.getKey entry))
25
- (clojurize (.getValue entry))))
26
- (persistent! acc))))
27
- RubyArray
28
- (clojurize [^RubyArray this]
29
- (loop [[entry & entries] (seq this)
30
- acc (transient [])]
31
- (if entry
32
- (recur entries (conj! acc (clojurize entry)))
33
- (persistent! acc)))))
2
+ (:require [zweikopf.multi :as multi])
3
+ (:import [org.jruby.embed ScriptingContainer LocalContextScope]
4
+ [org.jruby Ruby]))
5
+
6
+ (declare ^ScriptingContainer ruby)
7
+ (declare ^Ruby ruby-runtime)
8
+
9
+ (defn clojurize
10
+ [this]
11
+ (multi/clojurize this ruby))
12
+
13
+ (defn rubyize
14
+ [this]
15
+ (multi/rubyize this ruby))
16
+
17
+ (defn ruby-eval
18
+ [script]
19
+ (multi/ruby-eval ruby script))
20
+
21
+ (defn ruby-require
22
+ [lib]
23
+ (ruby-eval (format "require '%s'" lib)))
24
+
25
+ (defn ruby-load
26
+ [lib]
27
+ (ruby-eval (format "load '%s'" lib)))
28
+
29
+ (defn call-ruby
30
+ [& args]
31
+ (apply multi/call-ruby ruby args))
32
+
33
+ (defn set-gem-path
34
+ "Sets GEM_PATH Environment variable"
35
+ [gem-path]
36
+ (ruby-eval (str "ENV['GEM_PATH']='" gem-path "'")))
37
+
38
+ (defn set-gem-home
39
+ [gem-home]
40
+ (ruby-eval (str "ENV['GEM_HOME']='" gem-home "'")))
41
+
42
+ (defn init-ruby-context
43
+ []
44
+ (defonce ruby (ScriptingContainer. LocalContextScope/SINGLETON))
45
+ (defonce ruby-runtime (.getRuntime (.getProvider ruby))))
@@ -0,0 +1,145 @@
1
+ (ns zweikopf.multi
2
+ (:require [clojure.string :as str])
3
+ (:import (org.jruby.embed ScriptingContainer
4
+ LocalContextScope)
5
+ (org.jruby Ruby
6
+ RubyArray
7
+ RubyBasicObject
8
+ RubyHash
9
+ RubyHash$RubyHashEntry
10
+ RubyObject
11
+ RubyRational
12
+ RubyString
13
+ RubySymbol)))
14
+
15
+ (defprotocol Clojurize
16
+ (clojurize [this ^ScriptingContainer ruby]))
17
+
18
+ (defprotocol Rubyize
19
+ (rubyize [this ^ScriptingContainer ruby]))
20
+
21
+ (defn- ^Ruby runtime
22
+ [^ScriptingContainer container]
23
+ (-> container .getProvider .getRuntime))
24
+
25
+ (defn ruby-eval
26
+ [^ScriptingContainer ruby script]
27
+ (.runScriptlet ruby script))
28
+
29
+ (defn call-ruby
30
+ [^ScriptingContainer container klass method & args]
31
+ (let [method-name (name method)
32
+ klass (if (string? klass)
33
+ (ruby-eval container (str/replace klass "/" "::"))
34
+ klass)]
35
+ (if (empty? args)
36
+ (.callMethod container klass method-name Object)
37
+ (.callMethod container klass method-name (object-array args) Object))))
38
+
39
+ (defn context
40
+ []
41
+ (ScriptingContainer. LocalContextScope/SINGLETHREAD))
42
+
43
+ (defn terminate
44
+ [^ScriptingContainer ctx]
45
+ (.terminate ctx))
46
+
47
+ (extend-protocol Clojurize
48
+ nil
49
+ (clojurize [this _]
50
+ nil)
51
+
52
+ RubySymbol
53
+ (clojurize [this _]
54
+ (clojure.lang.Keyword/intern (.toString this)))
55
+
56
+ RubyHash
57
+ (clojurize [this ruby]
58
+ (persistent!
59
+ (reduce (fn [acc ^RubyHash$RubyHashEntry entry]
60
+ (assoc! acc
61
+ (clojurize (.getKey entry) ruby)
62
+ (clojurize (.getValue entry) ruby)))
63
+ (transient {})
64
+ (.directEntrySet this))))
65
+
66
+ RubyArray
67
+ (clojurize [this ruby]
68
+ (mapv #(clojurize % ruby) this))
69
+
70
+ RubyString
71
+ (clojurize [this _]
72
+ (.decodeString this))
73
+
74
+ org.jruby.RubyNil
75
+ (clojurize [_ _]
76
+ nil)
77
+
78
+ org.jruby.RubyRational
79
+ (clojurize [this ruby]
80
+ (let [context (.getCurrentContext (runtime ruby))
81
+ numerator (clojurize (.numerator this context) ruby)
82
+ denominator (clojurize (.denominator this context) ruby)]
83
+ (/ numerator denominator)))
84
+
85
+ org.jruby.RubyFixnum
86
+ (clojurize [this _]
87
+ (.getLongValue this))
88
+
89
+ org.jruby.RubyFloat
90
+ (clojurize [this _]
91
+ (.getDoubleValue this))
92
+
93
+ org.jruby.RubyBoolean
94
+ (clojurize [this _]
95
+ (.isTrue this))
96
+
97
+ org.jruby.RubyTime
98
+ (clojurize [this _]
99
+ (.toJava this java.util.Date))
100
+
101
+ org.jruby.RubyObject
102
+ (clojurize [this ruby]
103
+ (condp #(call-ruby ruby %2 :respond_to? %1) this
104
+ "to_hash" (clojurize (call-ruby ruby this :to_hash) ruby)
105
+ "strftime" (-> (call-ruby ruby this :strftime "%s")
106
+ Long/parseLong
107
+ (* 1000)
108
+ java.util.Date.)))
109
+
110
+ java.lang.Object
111
+ (clojurize [this _]
112
+ this))
113
+
114
+ (defn- apply-to-keys-and-values [m f]
115
+ (into {} (for [[k v] m]
116
+ [(f k) (f v)])))
117
+
118
+ (extend-protocol Rubyize
119
+ clojure.lang.IPersistentMap
120
+ (rubyize [this ruby]
121
+ (doto (RubyHash. (runtime ruby))
122
+ (.putAll (apply-to-keys-and-values this #(rubyize % ruby)))))
123
+
124
+ clojure.lang.Ratio
125
+ (rubyize [this ruby]
126
+ (RubyRational/newRational (runtime ruby)
127
+ (.numerator this)
128
+ (.denominator this)))
129
+
130
+ clojure.lang.Seqable
131
+ (rubyize [this ruby]
132
+ (doto (RubyArray/newArray (runtime ruby))
133
+ (.addAll (for [item this] (rubyize item ruby)))))
134
+
135
+ clojure.lang.Keyword
136
+ (rubyize [this ruby]
137
+ (.fastNewSymbol (runtime ruby) (name this)))
138
+
139
+ java.lang.Object
140
+ (rubyize [this _]
141
+ this)
142
+
143
+ nil
144
+ (rubyize [_ _]
145
+ nil))
@@ -3,21 +3,88 @@
3
3
  (:use clojure.test
4
4
  zweikopf.core))
5
5
 
6
- (defn run-ruby-script [script]
7
- (.runScriptlet (ScriptingContainer.) script))
6
+ (use-fixtures :once (fn [f]
7
+ (init-ruby-context)
8
+ (f)))
8
9
 
9
- (deftest hash-test
10
+
11
+ (deftest clojurize-test
12
+ (ruby-eval "require 'date'")
13
+ (testing "Numbers"
14
+ (testing "Integer"
15
+ (is (= 123 (clojurize (ruby-eval "123")))))
16
+ (testing "Floating point"
17
+ (is (= 123.45 (clojurize (ruby-eval "123.45")))))
18
+ (testing "Rational"
19
+ (is (= 1/3 (clojurize (ruby-eval "Rational(1,3)")))))
20
+ (testing "Big Decimals"
21
+ (is (= 12.34M (clojurize (ruby-eval "require 'bigdecimal'; BigDecimal.new(12.34,4)"))))))
10
22
  (testing "Empty hash"
11
- (is (= {} (clojurize (run-ruby-script "{}")))))
23
+ (is (= {} (clojurize (ruby-eval "{}")))))
12
24
  (testing "Non-empty hash"
13
- (is (= {:a 1 :b 2} (clojurize (run-ruby-script "{:a => 1, :b =>2 }")))))
25
+ (is (= {:a 1 :b 2} (clojurize (ruby-eval "{:a => 1, :b =>2 }")))))
26
+ (testing "Deep hash"
27
+ (is (= {:a 1 :b {:c 3 :d 4}} (clojurize (ruby-eval "{:a => 1, :b => {:c => 3, :d =>4}}")))))
28
+ (testing "Empty array"
29
+ (is (= [] (clojurize (ruby-eval "[]")))))
30
+ (testing "Non-empty array"
31
+ (is (= [1 :a "b"]) (clojurize (ruby-eval "[1, :a, 'b']"))))
32
+ (testing "Deep array"
33
+ (is (= [:a [:c :d] :e :f]) (clojurize (ruby-eval "[:a, [:c, :d], :e, :f]"))))
34
+ (testing "Complex DS"
35
+ (is (= [:a [:c {:d :e}] :f :g] (clojurize (ruby-eval "[:a, [:c, {:d => :e}], :f, :g]")))))
36
+ (testing "Times"
37
+ (testing "DateTime"
38
+ (let [date (clojurize (ruby-eval "DateTime.new(2013,2,19,12,34,56)"))] ;; => Tue, 19 Feb 2013 12:34:56 +0000
39
+ (is (= java.util.Date (class date)))
40
+ (is (= #inst "2013-02-19T12:34:56" date))))))
41
+
42
+ (deftest rubyize-test
43
+ (testing "Numbers"
44
+ (testing "Integer"
45
+ (is (= (ruby-eval "123")
46
+ (rubyize 123))))
47
+ (testing "Floating point"
48
+ (is (= (ruby-eval "123.45")
49
+ (rubyize 123.45))))
50
+ (testing "Rational"
51
+ (is (= (ruby-eval "Rational(1,3)")
52
+ (rubyize 1/3))))
53
+ (testing "Big Decimals"
54
+ (is (= (ruby-eval "require 'bigdecimal'; BigDecimal.new(12.34,4)")
55
+ (rubyize 12.34M)))))
56
+ (testing "Emtpy hash"
57
+ (is (.equals (ruby-eval "{}") (rubyize {}))))
58
+ (testing "Non-emtpy hash"
59
+ (is (.equals (ruby-eval "{:a => 1, :b => 2}") (rubyize {:a 1 :b 2}))))
14
60
  (testing "Deep hash"
15
- (is (= {:a 1 :b {:c 3 :d 4}} (clojurize (run-ruby-script "{:a => 1, :b => {:c => 3, :d =>4}}"))))))
61
+ (is (.equals (ruby-eval "{:a => 1, :b => {:c => 3, :d =>4}}") (rubyize {:a 1 :b {:c 3 :d 4}}))))
16
62
 
17
- (deftest array-test
18
63
  (testing "Empty array"
19
- (is (= [] (clojurize (run-ruby-script "[]")))))
64
+ (is (= (ruby-eval "[]") (rubyize []))))
65
+
66
+ (testing "Empty list"
67
+ (is (= (ruby-eval "[]") (rubyize '()))))
68
+
20
69
  (testing "Non-empty array"
21
- (is (= [1 :a "b"]) (run-ruby-script "[1, :a, 'b']")))
70
+ (is (= (ruby-eval "[1, :a, 'b']") (rubyize [1 :a "b"])) ))
71
+
72
+ (testing "Lazy seq"
73
+ (is (= (ruby-eval "[:a, :b, :c]") (rubyize (lazy-seq [:a :b :c])))))
74
+
22
75
  (testing "Deep array"
23
- (is (= [:a [:c :d] :e :f]) (run-ruby-script "[:a, [:c, :d], :e, :f]"))))
76
+ (is (= (ruby-eval "[:a, [:c, :d], :e, :f]") (rubyize [:a [:c :d] :e :f])) ))
77
+
78
+ (testing "Complex DS"
79
+ (is (= (ruby-eval "[:a, [:c, {:d => :e}], :f, :g]") (rubyize [:a [:c {:d :e}] :f :g])))))
80
+
81
+ (deftest performance
82
+ (testing "this spec simply shows performance, very roughly, please do your testing depending on your needs"
83
+ (time
84
+ (let [ruby-obj (ruby-eval "{:a => 1, :b => 2 }")]
85
+ (dotimes [i 100000]
86
+ (clojurize ruby-obj))))
87
+ (time
88
+ (let [clj-obj {:a 1 :b 2}]
89
+ (dotimes [i 100000]
90
+ (rubyize clj-obj))))))
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: zweikopf
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.0.6
5
+ version: 0.4.0
6
6
  platform: ruby
7
7
  authors:
8
8
  - Oleksandr Petrov
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2012-12-07 00:00:00 Z
13
+ date: 2014-01-27 00:00:00 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rake
@@ -72,6 +72,7 @@ files:
72
72
  - spec/zweikopf/transformer_clojure_to_ruby_spec.rb
73
73
  - spec/zweikopf/transformer_ruby_to_clojure_spec.rb
74
74
  - src/zweikopf/core.clj
75
+ - src/zweikopf/multi.clj
75
76
  - test/zweikopf/core_test.clj
76
77
  - zweikopf.gemspec
77
78
  homepage: ""