zweikopf 0.0.6 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: ""