transparent-lua 0.1

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.
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ NThmMTMyNjhhM2Y1YTJlYjk2NGZhMmM1MzVkOGJmZDY2YTMzOWFjOQ==
5
+ data.tar.gz: !binary |-
6
+ NTYzNzE4MzU5YzkxODYwMjk5Y2UxNjE4YzQ5MWViNmM2NWFmN2ZjNw==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ NzQxODUwYjIzYWFiY2Y4OGFlY2E4ZGQ2NGYzMWE4NDRmMGFlNGQzNzk2MDdl
10
+ NWZhZDI0MTk4NjkyYzA5MGY2NGFhZDAxZDMxZDAyMWU5ZmNiMDU5YzdlYjY5
11
+ M2ZhNDZlZjJjNDBlOGJiODI2Y2FlNTI4ZTYzODAwOTUwN2Y5NDU=
12
+ data.tar.gz: !binary |-
13
+ NGViNzA2ZWU0ZTEzZWU3NjJjNmU2ZjJkNGYwMDk0YzQxYjY3OWM0ZDcxYTg2
14
+ Nzc5MDczZTBhYzA0NDg1YzMxYTQwYjI0Zjk2ZTQzYWZkZGRiZjlmMTg0ZmI5
15
+ ZjhkZjczODMyMDI4ZjFjY2FiZThjZDhmNGY3YjQ5ZmYxMTI0NjE=
@@ -0,0 +1,8 @@
1
+ /.idea
2
+ /tmp
3
+ *.gem
4
+ Gemfile.lock
5
+ .ruby-version
6
+ .rspec_result
7
+
8
+
@@ -0,0 +1,15 @@
1
+ language: ruby
2
+ rvm:
3
+ - ruby-head
4
+ - 2.2-head
5
+ - 2.1.1
6
+ - 1.9.3
7
+ matrix:
8
+ allow_failures:
9
+ - rvm: ruby-head
10
+ - rvm: 2.2-head
11
+
12
+ sudo: true
13
+ before_install:
14
+ - sudo apt-get update -qq
15
+ - sudo apt-get install -y liblua5.1-dev
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ group :test do
6
+ gem 'guard'
7
+ gem 'guard-cucumber'
8
+ gem 'guard-rspec'
9
+ gem 'libnotify'
10
+ end
@@ -0,0 +1,25 @@
1
+ guard :rspec,
2
+ cmd: 'bundle exec rspec --fail-fast --color',
3
+ all_on_start: true,
4
+ all_after_pass: true do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
7
+ watch('spec/spec_helper.rb') { 'spec' }
8
+ notification :file,
9
+ path: '.rspec_result',
10
+ format: '%s'
11
+ end
12
+
13
+
14
+ guard :cucumber,
15
+ all_on_start: true do
16
+ watch '.rspec_result' do
17
+ 'features' if File.read('.rspec_result').strip == 'success'
18
+ end
19
+ watch(%r{^features/.+\.feature$})
20
+ watch(%r{^features/support/.+$}) { 'features' }
21
+
22
+ watch(%r{^features/step_definitions/(.+)_steps\.rb$}) do |m|
23
+ Dir[File.join("**/#{m[1]}.feature")][0] || 'features'
24
+ end
25
+ end
@@ -0,0 +1,80 @@
1
+ # Description
2
+ This library makes it easy to provide a complex API for Lua scripts. It
3
+ uses [RLua](https://github.com/whitequark/rlua) internally and therefore it
4
+ has the same compatibility and restrictions.
5
+
6
+ # Example code
7
+ Lets say we have these classes:
8
+
9
+ ```ruby
10
+ User = Struct.new(:name, :favorite_food, :age)
11
+ Food = Struct.new(:name, :cooking_time) do
12
+ def prepare(timer)
13
+ if timer == cooking_time
14
+ "This #{name} is delicious"
15
+ elsif timer > cooking_time
16
+ "This #{name} is awfully burnt"
17
+ else
18
+ "Smells like frozen #{name}..."
19
+ end
20
+ end
21
+
22
+ def buy(*)
23
+ 'Got one'
24
+ end
25
+ end
26
+
27
+ Pizza = Food.new('Pizza', 16)
28
+ Lasagne = Food.new('Lasagne', 40)
29
+
30
+ class Sandbox
31
+ def get(username)
32
+ users.detect { |u| u.name == username }
33
+ end
34
+
35
+ def users
36
+ [
37
+ User.new('Kyle', Pizza, 43),
38
+ User.new('Lisa', Pizza, 25),
39
+ User.new('Ben', Lasagne, 6),
40
+ ]
41
+ end
42
+ end
43
+ ```
44
+
45
+ To make it available to our Lua script we can use this code:
46
+
47
+ ```ruby
48
+ require 'transparent_lua'
49
+
50
+ tlua = TransparentLua.new(Sandbox.new)
51
+ tlua.call(<<-EOF)
52
+ print(get_user('Kyle').name .. " likes " .. get_user('Kyle').favorite_food.name .. ".");
53
+ print(get_user('Kyle').favorite_food.buy());
54
+ print("It needs to cook exactly " .. get_user('Kyle').favorite_food.cooking_time .. " minutes");
55
+
56
+ my_cooking_time = 270;
57
+ print(get_user('Kyle').name .. " cooks it for " .. my_cooking_time .. " minutes.");
58
+ print(get_user('Kyle').favorite_food.prepare(my_cooking_time));
59
+ EOF
60
+
61
+ Pizza.cooking_time = 270
62
+
63
+ tlua.call(<<-EOF)
64
+ print("Lets try it again for " .. my_cooking_time .. " minutes. Maybe it works now...");
65
+
66
+ print(get_user('Kyle').name .. " cooks it for " .. my_cooking_time .. " minutes.");
67
+ print(get_user('Kyle').favorite_food.prepare(my_cooking_time));
68
+ EOF
69
+ ```
70
+
71
+ # Types of methods
72
+ * Methods without arguments are created as index of the Lua table
73
+ * Methods with arguments are created as a callable metatable.
74
+ * To make clear, that an argumentless method is a method (`food.buy()` vs. `food.buy`),
75
+ discard any arguments (`def buy(*)`)
76
+
77
+ # Type conversion
78
+ In addition to RLuas type conversion, this library converts Lua tables to either Hashes or
79
+ Arrays (when all keys are Numeric).
80
+
@@ -0,0 +1,7 @@
1
+ require 'rspec/core/rake_task'
2
+ require 'cucumber/rake/task'
3
+
4
+ RSpec::Core::RakeTask.new
5
+ Cucumber::Rake::Task.new
6
+
7
+ task :default => [ :cucumber, :spec ]
@@ -0,0 +1,65 @@
1
+ Feature: Sandbox exposition
2
+
3
+ Scenario: Exposing sandbox getters to the lua script
4
+ Given an empty sandbox
5
+ And the following method definition as part of the sandbox: def meth; 'OK'; end;
6
+ And the following line as Lua script: return(meth);
7
+ When I execute the script
8
+ Then it should return "OK"
9
+
10
+ Scenario Outline: Exposing member class methods to the lua script
11
+ Given an sandbox with an empty member class
12
+ And the following method definition as part of the member class: def <method_signature>; <method_body>; end;
13
+ And the following line as Lua script: return(<method_call>);
14
+ When I execute the script
15
+ Then it should return "OK"
16
+ Examples:
17
+ | method_signature | method_body | method_call |
18
+ | meth | 'OK' | member_class.meth |
19
+ | meth(arg1, arg2) | [arg1, arg2].pack("U*") | member_class.meth(79, 75) |
20
+ | meth(arg1, arg2 = 75) | [arg1, arg2].pack("U*") | member_class.meth(79) |
21
+ | meth(*args) | 'OK' | member_class.meth() |
22
+ | meth(*) | 'OK' | member_class.meth() |
23
+
24
+ Scenario: Returning values
25
+ Given an empty sandbox
26
+ And the following line as Lua script: return('Hello');
27
+ When I execute the script
28
+ Then it should return "Hello"
29
+
30
+ Scenario Outline: Returning values
31
+ Given an empty sandbox
32
+ And the following line as Lua script: return(<lua_value>);
33
+ When I execute the script
34
+ Then it should return <return_value>
35
+ Examples:
36
+ | lua_value | return_value |
37
+ | 5 | 5 |
38
+ | 5.1 | 5.1 |
39
+ | "Hallo" | "Hallo" |
40
+ | { ["Foo"] = "Bar" } | {"Foo" => "Bar"} |
41
+ | {"A", "B", "C"} | ['A', 'B', 'C'] |
42
+ | true | true |
43
+
44
+ Scenario Outline: Passing values to methods
45
+ Given an empty sandbox
46
+ And the following method definition as part of the sandbox: attr_accessor :attrib
47
+ And the following line as Lua script: attrib = <lua_value>
48
+ When I execute the script with locales leaking enabled
49
+ Then the attribute "attrib" of the sandbox is <return_value>
50
+ Examples:
51
+ | lua_value | return_value |
52
+ | 5 | 5 |
53
+ | 5.1 | 5.1 |
54
+ | "Hallo" | "Hallo" |
55
+ | { ["Foo"] = "Bar" } | {"Foo" => "Bar"} |
56
+ | {"A", "B", "C"} | ['A', 'B', 'C'] |
57
+ | true | true |
58
+
59
+ Scenario: Calling Lua functions
60
+ Given an empty sandbox
61
+ And the following line as Lua script: return(string.upper('Hello'));
62
+ When I execute the script
63
+ Then it should return "HELLO"
64
+
65
+
@@ -0,0 +1,47 @@
1
+ require 'transparent_lua'
2
+
3
+ Given(/^an empty sandbox$/) do
4
+ @sandbox_class = Class.new do
5
+ def inspect
6
+ '#<Empty Sandbox Class>'
7
+ end
8
+
9
+ alias :to_s :inspect
10
+ end
11
+ end
12
+
13
+ Given(/^an sandbox with an empty member class$/) do
14
+ @member_class = Class.new do
15
+ def inspect
16
+ '#<Sandbox Member Class>'
17
+ end
18
+
19
+ alias :to_s :inspect
20
+ end
21
+
22
+ @sandbox_class = Class.new do
23
+ def inspect
24
+ '#<Sandbox Class>'
25
+ end
26
+
27
+ alias :to_s :inspect
28
+ end
29
+
30
+ @sandbox_class.class_exec(@member_class) do |member_class|
31
+ define_method(:member_class) { member_class.new }
32
+ end
33
+ end
34
+
35
+ And(/^the following method definition as part of the sandbox: (.*)$/) do |method_definition|
36
+ @sandbox_class.class_eval(method_definition)
37
+ end
38
+
39
+ And(/^the following method definition as part of the member class: (.*)$/) do |method_definition|
40
+ @member_class.class_eval(method_definition)
41
+ end
42
+
43
+ And(/^the following line as Lua script: (.*)$/) do |script|
44
+ @script = String(script).strip
45
+ end
46
+
47
+
@@ -0,0 +1,11 @@
1
+ When(/^I execute the script$/) do
2
+ @transparent_lua = TransparentLua.new(@sandbox = @sandbox_class.new)
3
+
4
+ @return_value = @transparent_lua.call(@script)
5
+ end
6
+
7
+ When(/^I execute the script with locales leaking enabled$/) do
8
+ @transparent_lua = TransparentLua.new(@sandbox = @sandbox_class.new, leak_globals: true)
9
+
10
+ @return_value = @transparent_lua.call(@script)
11
+ end
@@ -0,0 +1,11 @@
1
+ Then(/^it should return (.*)$/) do |expected_return_value|
2
+ expected_return_value = eval(expected_return_value)
3
+ expect(@return_value).to eq(expected_return_value)
4
+ end
5
+
6
+
7
+ Then(/^the attribute "([^"]*)" of the sandbox is (.*)$/) do |attribute, expected_return_value|
8
+ expected_return_value = eval(expected_return_value)
9
+
10
+ expect(@sandbox.public_send(attribute.to_sym)).to eq(expected_return_value)
11
+ end
@@ -0,0 +1,116 @@
1
+ require 'rlua'
2
+
3
+ class TransparentLua
4
+ SUPPORTED_SIMPLE_DATATYPES = [
5
+ NilClass,
6
+ TrueClass,
7
+ FalseClass,
8
+ Fixnum,
9
+ Bignum,
10
+ Float,
11
+ Proc,
12
+ String,
13
+ Hash,
14
+ Array,
15
+ ]
16
+
17
+ attr_reader :sandbox, :state
18
+
19
+ # @param [Object] sandbox The object which will be made visible to the lua script
20
+ # @param [Hash] options
21
+ # @option options [Lua::State] :state (Lua::State.new) a lua state to use
22
+ # @option options [Boolean] :leak_globals When true, all locals from the lua scope are set in the sandbox.
23
+ # The sandbox must store the values itself or an error will be raised.
24
+ # When false the locals are not reflected in the sandbox
25
+ def initialize(sandbox, options = {})
26
+ @sandbox = sandbox
27
+ @state = options.fetch(:state) { Lua::State.new }
28
+ leak_locals = options.fetch(:leak_globals) { false }
29
+ setup(leak_locals)
30
+ end
31
+
32
+ # @param [String] script a lua script
33
+ # @return [Object] the return value of the lua script
34
+ def call(script)
35
+ v = state.__eval script
36
+ lua2rb(v)
37
+ end
38
+
39
+ private
40
+ def setup(leak_globals = false)
41
+ state.__load_stdlib :all
42
+
43
+ global_metatable = {
44
+ '__index' => index_table(sandbox)
45
+ }
46
+ global_metatable['__newindex'] = newindex_table(sandbox) if leak_globals
47
+
48
+ state._G.__metatable = global_metatable
49
+ end
50
+
51
+ def getter_table(object)
52
+ if SUPPORTED_SIMPLE_DATATYPES.include? object.class
53
+ return object
54
+ end
55
+
56
+ metatable = { '__index' => index_table(object) }
57
+ metatable(metatable)
58
+ end
59
+
60
+ def newindex_table(object)
61
+ ->(t, k, v) do
62
+ getter_table(object.public_send(:"#{k}=", lua2rb(v)))
63
+ end
64
+ end
65
+
66
+ def index_table(object)
67
+ ->(t, k, *newindex_args) do
68
+ method = get_method(object, k)
69
+
70
+ case method
71
+ when ->(m) { m.arity == 0 }
72
+ # No or mandatory arguments
73
+ getter_table(object.public_send(k.to_sym, *newindex_args))
74
+ # when ->(m) { m.parameters == [[:rest]] }
75
+ # # Forced to be a method
76
+ # method_table(method)
77
+ # when ->(m) { m.arity < 0 }
78
+ # warn "Method #{method} has optional parameters. We dont like that"
79
+ # method_table(method)
80
+ else
81
+ method_table(method)
82
+ end
83
+ end
84
+ end
85
+
86
+ def get_method(object, method_name)
87
+ fail NoMethodError, "#{object}##{method_name.to_s} is not a method (but might me a valid message which is not supported)" unless object.methods.include? method_name.to_sym
88
+ object.method(method_name.to_sym)
89
+ end
90
+
91
+ # @param [Method] method
92
+ def method_table(method)
93
+ metatable(
94
+ '__call' => ->(t, *args) do
95
+ getter_table(method.call(*args))
96
+ end
97
+ )
98
+ end
99
+
100
+ def metatable(hash)
101
+ tab = Lua::Table.new(@state)
102
+ tab.__metatable = hash
103
+ tab
104
+ end
105
+
106
+ def lua2rb(v)
107
+ case v
108
+ when ->(t) { Lua::Table === t && t.to_hash.keys.all? { |k| k.is_a? Numeric } }
109
+ v.to_hash.values
110
+ when Lua::Table
111
+ v.to_hash
112
+ else
113
+ v
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,3 @@
1
+ class TransparentLua
2
+ VERSION = 0.1
3
+ end
File without changes
@@ -0,0 +1,5 @@
1
+ require 'spec_helper'
2
+ require 'transparent_lua'
3
+
4
+ describe TransparentLua do
5
+ end
@@ -0,0 +1,30 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib/', __FILE__)
3
+ $:.unshift lib unless $:.include?(lib)
4
+
5
+ require 'transparent_lua/version'
6
+
7
+ Gem::Specification.new do |gem|
8
+ gem.name = 'transparent-lua'
9
+ gem.version = TransparentLua::VERSION
10
+ gem.authors = ['Christian Haase']
11
+ gem.email = ['ruby@eggchamber.net']
12
+ gem.description = %q{A wrapper to pass complex objects between Ruby and Lua}
13
+ gem.summary = <<-EOD
14
+ This library enables an easy way to pass complex objects between
15
+ Ruby and Lua in a transparent way.
16
+ EOD
17
+ gem.homepage = 'https://github.com/krissi/ruby-transparent-lua'
18
+ gem.license = 'MIT'
19
+
20
+ gem.files = `git ls-files`.split($/)
21
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
22
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
23
+ gem.require_paths = ['lib']
24
+
25
+ gem.add_dependency 'rlua', '~> 1.0'
26
+
27
+ gem.add_development_dependency 'rake', '~> 10.4'
28
+ gem.add_development_dependency 'cucumber', '~> 2.0'
29
+ gem.add_development_dependency 'rspec', '~> 3.3'
30
+ end
metadata ADDED
@@ -0,0 +1,123 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: transparent-lua
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ platform: ruby
6
+ authors:
7
+ - Christian Haase
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-07-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rlua
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '10.4'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '10.4'
41
+ - !ruby/object:Gem::Dependency
42
+ name: cucumber
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '2.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '2.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '3.3'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '3.3'
69
+ description: A wrapper to pass complex objects between Ruby and Lua
70
+ email:
71
+ - ruby@eggchamber.net
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - .gitignore
77
+ - .travis.yml
78
+ - Gemfile
79
+ - Guardfile
80
+ - README.md
81
+ - Rakefile
82
+ - features/sandbox_exposition.feature
83
+ - features/step_definitions/preparation_steps.rb
84
+ - features/step_definitions/transparent_lua_steps.rb
85
+ - features/step_definitions/validation_steps.rb
86
+ - lib/transparent_lua.rb
87
+ - lib/transparent_lua/version.rb
88
+ - spec/spec_helper.rb
89
+ - spec/transparent_lua_spec.rb
90
+ - transparent-lua.gemspec
91
+ homepage: https://github.com/krissi/ruby-transparent-lua
92
+ licenses:
93
+ - MIT
94
+ metadata: {}
95
+ post_install_message:
96
+ rdoc_options: []
97
+ require_paths:
98
+ - lib
99
+ required_ruby_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ! '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ! '>='
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ requirements: []
110
+ rubyforge_project:
111
+ rubygems_version: 2.4.3
112
+ signing_key:
113
+ specification_version: 4
114
+ summary: This library enables an easy way to pass complex objects between Ruby and
115
+ Lua in a transparent way.
116
+ test_files:
117
+ - features/sandbox_exposition.feature
118
+ - features/step_definitions/preparation_steps.rb
119
+ - features/step_definitions/transparent_lua_steps.rb
120
+ - features/step_definitions/validation_steps.rb
121
+ - spec/spec_helper.rb
122
+ - spec/transparent_lua_spec.rb
123
+ has_rdoc: