From d913a157c69ddced09f84d56e98a71c15608c930 Mon Sep 17 00:00:00 2001 From: John Woods Date: Thu, 7 Apr 2016 09:27:55 -0700 Subject: [PATCH] Fixes #33. Adds SymEngine::symbols() method which produces multiple symbols simultaneously. Allows Symbol.new to take a Ruby symbol or a string. --- ext/symengine/ruby_symbol.c | 18 ++++++++++--- lib/symengine.rb | 33 ++++++++++++++++++++++++ spec/symbol_spec.rb | 51 +++++++++++++++++++++++++++++++++++++ 3 files changed, 98 insertions(+), 4 deletions(-) diff --git a/ext/symengine/ruby_symbol.c b/ext/symengine/ruby_symbol.c index e50f4e4..7e3bb47 100644 --- a/ext/symengine/ruby_symbol.c +++ b/ext/symengine/ruby_symbol.c @@ -1,10 +1,20 @@ #include "ruby_symbol.h" -VALUE csymbol_init(VALUE self, VALUE name) { - Check_Type(name, T_STRING); - basic_struct *this; - char *str_ptr = StringValueCStr(name); +VALUE csymbol_init(VALUE self, VALUE name_or_id) { + char *str_ptr; + + switch (TYPE(name_or_id)) { + case T_STRING: + str_ptr = StringValueCStr(name_or_id); + break; + case T_SYMBOL: + str_ptr = rb_id2name(rb_to_id(name_or_id)); + break; + default: + rb_raise(rb_eTypeError, "wrong argument type %s (expected Symbol or String)", rb_obj_classname(name_or_id)); + } + basic_struct *this; Data_Get_Struct(self, basic_struct, this); symbol_set(this, str_ptr); diff --git a/lib/symengine.rb b/lib/symengine.rb index 2a4b332..b454130 100644 --- a/lib/symengine.rb +++ b/lib/symengine.rb @@ -1,3 +1,36 @@ +module SymEngine + class << self + + # Defines a shortcut for SymEngine::Symbol.new() allowing multiple symbols + # to be created all at once. + # + # Examples: + # + # x, y = SymEngine.symbols(%i{x y}) + # x, y = SymEngine.symbols(%w{x y}) + # x, y = SymEngine.symbols([:x, :y]) + # x, y = SymEngine.symbols(['x', 'y']) + # x, y = SymEngine.symbols('x', 'y') + # x, y = SymEngine.symbols('x y') + # + def symbols ary_or_string, *params + # Want to make sure we can accept an array or a bunch of splatted arguments + if params.size > 0 + ary_or_string = (ary_or_string.is_a?(String) ? [ary_or_string] : ary_or_string).concat(params) + elsif ary_or_string.is_a?(String) + # or accept a string + ary_or_string = ary_or_string.split + end + + # Make an Array of SymEngine::Symbols from the parameters we received, + # now that they're normalized. + ary_or_string.map do |symbol_or_string| + SymEngine::Symbol.new(symbol_or_string) + end + end + end +end + require 'symengine/symengine' require 'symengine/iruby' require 'symengine/basic' diff --git a/spec/symbol_spec.rb b/spec/symbol_spec.rb index 80e4556..6af8564 100644 --- a/spec/symbol_spec.rb +++ b/spec/symbol_spec.rb @@ -1,9 +1,53 @@ require 'spec_helper' + + describe SymEngine do before :each do end + describe '.symbols' do + before :all do + + # Create a shortcut Proc to DRY up code. + # + # Call this with the number of symbols we expect to get out + # and the argument we want to provide it. + @test_symbols_method = Proc.new do |count_symbols, *args| + symbols = SymEngine.symbols(*args) + symbols.each do |symbol| + expect(symbol).to be_an_instance_of SymEngine::Symbol + end + expect(symbols.size).to eql(count_symbols) + end + end + + context 'with a white-space separated string as an argument' do + it 'returns an Enumerable of Symbol objects' do + @test_symbols_method.call(2, 'x y') + end + end + + context 'with an array of Ruby Symbols as an argument' do + it 'returns an Enumerable of Symbol objects' do + # @test_symbols_method.call(2, %i{x y}) # Ruby 2.0 and higher only + @test_symbols_method.call(2, [:x, :y]) + end + end + + context 'with an array of Ruby Strings as an argument' do + it 'returns an Enumerable of Symbol objects' do + @test_symbols_method.call(2, %w{x y}) + end + end + + context 'with a splatted argument' do + it 'returns an Enumerable of Symbol objects' do + @test_symbols_method.call(2, 'x', 'y') + end + end + end + describe SymEngine::Symbol do before :each do end @@ -15,6 +59,13 @@ expect(symbol).to be_an_instance_of SymEngine::Symbol end end + + context 'with a Ruby symbol as an argument' do + it 'returns a Symbol object' do + symbol = SymEngine::Symbol.new(:x) + expect(symbol).to be_an_instance_of SymEngine::Symbol + end + end end end end