<commit_before>package org.typeexit.kettle.plugin.steps.ruby.execmodels;

import java.io.File;
import java.io.StringReader;
import java.math.BigDecimal;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;

import org.apache.commons.lang.ArrayUtils;
import org.jruby.RubyArray;
import org.jruby.RubyFixnum;
import org.jruby.RubyFloat;
import org.jruby.RubyHash;
import org.jruby.RubyThread;
import org.jruby.RubyTime;
import org.jruby.embed.EvalFailedException;
import org.jruby.exceptions.ThreadKill;
import org.jruby.javasupport.JavaEmbedUtils;
import org.jruby.javasupport.JavaUtil;
import org.jruby.runtime.builtin.IRubyObject;
import org.pentaho.di.core.Const;
import org.pentaho.di.core.exception.KettleException;
import org.pentaho.di.core.row.RowDataUtil;
import org.pentaho.di.core.row.RowMeta;
import org.pentaho.di.core.row.RowMetaInterface;
import org.pentaho.di.core.row.ValueMeta;
import org.pentaho.di.core.row.ValueMetaInterface;
import org.pentaho.di.trans.step.errorhandling.StreamInterface;
import org.typeexit.kettle.plugin.steps.ruby.RubyStep;
import org.typeexit.kettle.plugin.steps.ruby.RubyStepData;
import org.typeexit.kettle.plugin.steps.ruby.RubyStepFactory;
import org.typeexit.kettle.plugin.steps.ruby.RubyStepMarshalledObject;
import org.typeexit.kettle.plugin.steps.ruby.RubyStepMeta;
import org.typeexit.kettle.plugin.steps.ruby.meta.RubyScriptMeta;
import org.typeexit.kettle.plugin.steps.ruby.meta.RubyVariableMeta;
import org.typeexit.kettle.plugin.steps.ruby.streams.BufferStreamReader;
import org.typeexit.kettle.plugin.steps.ruby.streams.ErrorStreamWriter;
import org.typeexit.kettle.plugin.steps.ruby.streams.StdStreamReader;
import org.typeexit.kettle.plugin.steps.ruby.streams.StdStreamWriter;
import org.typeexit.kettle.plugin.steps.ruby.streams.StepStreamReader;
import org.typeexit.kettle.plugin.steps.ruby.streams.StepStreamWriter;

public class SimpleExecutionModel implements ExecutionModel {

	private RubyStepData data;
	private RubyStepMeta meta;
	private RubyStep step;

	@Override
	public void setEnvironment(RubyStep step, RubyStepData data, RubyStepMeta meta) {
		this.data = data;
		this.meta = meta;
		this.step = step;
	}

	@Override
	public boolean onInit() {

		try {

			data.forcedHalt = false;

			data.container = RubyStepFactory.createScriptingContainer(true, meta.getRubyVersion());

			data.runtime = data.container.getProvider().getRuntime();

			// set gem home if specified
			setGemHome();

			data.container.setScriptFilename(meta.getRowScript().getTitle());
			data.rubyScriptObject = data.container.parse(meta.getRowScript().getScript(), 0);

			// put the usual stuff into global scope
			data.container.put("$step", step);
			data.container.put("$trans", step.getDispatcher());

			// put all variables into scope
			for (RubyVariableMeta var : meta.getRubyVariables()) {
				data.container.put(var.getName(), step.environmentSubstitute(var.getValue()));
			}

			// put all script tabs into scope
			RubyHash tabs = new RubyHash(data.runtime);

			for (RubyScriptMeta tab : meta.getScripts()) {
				tabs.put(tab.getTitle(), new ScriptTab(tab, data));
			}

			data.container.put("$tabs", tabs);

			// temporary place for the output a script might produce
			data.rowList = new LinkedList<Object[]>();

			// add << aliases to the java stream writers
			data.container.runScriptlet("JavaUtilities.extend_proxy('org.typeexit.kettle.plugin.steps.ruby.streams.StdStreamWriter') {alias << write}\n");
			data.container.runScriptlet("JavaUtilities.extend_proxy('org.typeexit.kettle.plugin.steps.ruby.streams.ErrorStreamWriter') {alias << write}\n");
			data.container.runScriptlet("JavaUtilities.extend_proxy('org.typeexit.kettle.plugin.steps.ruby.streams.StepStreamWriter') {alias << write}\n");

		} catch (Exception e) {
			step.logError("Error Initializing Ruby Scripting Step", e);
			return false;
		}

		return true;
	}

	private void setGemHome() {

		// if specified directly, take it
		String gemHomeString = step.environmentSubstitute(meta.getGemHome());

		// if not, fall back to RUBY_GEM_HOME
		if (Const.isEmpty(gemHomeString) && !Const.isEmpty(step.getVariable("RUBY_GEM_HOME"))) {
			gemHomeString = step.environmentSubstitute("${RUBY_GEM_HOME}");
		}

		// if that fails, use the standard one
		if (Const.isEmpty(gemHomeString)) {
			gemHomeString = step.getPluginDir() + Const.FILE_SEPARATOR + "gems";
		}

		if (!Const.isEmpty(gemHomeString)) {

			File gemHomePath = new File(gemHomeString);
			gemHomePath = gemHomePath.getAbsoluteFile();

			RubyHash configHash = (RubyHash) data.container.parse("require 'rbconfig'; RbConfig::CONFIG").run();<commit_msg>guard against RVM magic! RVM sets GEM_HOME and GEM_PATH environment variables which can mess up with our JRuby rubygems. See coming commits<commit_after>package org.typeexit.kettle.plugin.steps.ruby.execmodels;

import java.io.File;
import java.io.StringReader;
import java.math.BigDecimal;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.Collections;
import java.lang.reflect.Field;

import org.apache.commons.lang.ArrayUtils;
import org.jruby.RubyArray;
import org.jruby.RubyFixnum;
import org.jruby.RubyFloat;
import org.jruby.RubyHash;
import org.jruby.RubyThread;
import org.jruby.RubyTime;
import org.jruby.embed.EvalFailedException;
import org.jruby.exceptions.ThreadKill;
import org.jruby.javasupport.JavaEmbedUtils;
import org.jruby.javasupport.JavaUtil;
import org.jruby.runtime.builtin.IRubyObject;
import org.pentaho.di.core.Const;
import org.pentaho.di.core.exception.KettleException;
import org.pentaho.di.core.row.RowDataUtil;
import org.pentaho.di.core.row.RowMeta;
import org.pentaho.di.core.row.RowMetaInterface;
import org.pentaho.di.core.row.ValueMeta;
import org.pentaho.di.core.row.ValueMetaInterface;
import org.pentaho.di.trans.step.errorhandling.StreamInterface;
import org.typeexit.kettle.plugin.steps.ruby.RubyStep;
import org.typeexit.kettle.plugin.steps.ruby.RubyStepData;
import org.typeexit.kettle.plugin.steps.ruby.RubyStepFactory;
import org.typeexit.kettle.plugin.steps.ruby.RubyStepMarshalledObject;
import org.typeexit.kettle.plugin.steps.ruby.RubyStepMeta;
import org.typeexit.kettle.plugin.steps.ruby.meta.RubyScriptMeta;
import org.typeexit.kettle.plugin.steps.ruby.meta.RubyVariableMeta;
import org.typeexit.kettle.plugin.steps.ruby.streams.BufferStreamReader;
import org.typeexit.kettle.plugin.steps.ruby.streams.ErrorStreamWriter;
import org.typeexit.kettle.plugin.steps.ruby.streams.StdStreamReader;
import org.typeexit.kettle.plugin.steps.ruby.streams.StdStreamWriter;
import org.typeexit.kettle.plugin.steps.ruby.streams.StepStreamReader;
import org.typeexit.kettle.plugin.steps.ruby.streams.StepStreamWriter;

public class SimpleExecutionModel implements ExecutionModel {

	private RubyStepData data;
	private RubyStepMeta meta;
	private RubyStep step;

	@Override
	public void setEnvironment(RubyStep step, RubyStepData data, RubyStepMeta meta) {
		this.data = data;
		this.meta = meta;
		this.step = step;
	}

	@Override
	public boolean onInit() {

		try {

            HashMap envMap = new HashMap();
            envMap.put("GEM_HOME", new String(step.getPluginDir() + Const.FILE_SEPARATOR + "gems"));
            envMap.put("GEM_PATH", new String(step.getPluginDir() + Const.FILE_SEPARATOR + "gems"));
            setEnv(envMap);

			data.forcedHalt = false;

			data.container = RubyStepFactory.createScriptingContainer(true, meta.getRubyVersion());

			data.runtime = data.container.getProvider().getRuntime();

			// set gem home if specified
			setGemHome();

			data.container.setScriptFilename(meta.getRowScript().getTitle());
			data.rubyScriptObject = data.container.parse(meta.getRowScript().getScript(), 0);

			// put the usual stuff into global scope
			data.container.put("$step", step);
			data.container.put("$trans", step.getDispatcher());

			// put all variables into scope
			for (RubyVariableMeta var : meta.getRubyVariables()) {
				data.container.put(var.getName(), step.environmentSubstitute(var.getValue()));
			}

			// put all script tabs into scope
			RubyHash tabs = new RubyHash(data.runtime);

			for (RubyScriptMeta tab : meta.getScripts()) {
				tabs.put(tab.getTitle(), new ScriptTab(tab, data));
			}

			data.container.put("$tabs", tabs);

			// temporary place for the output a script might produce
			data.rowList = new LinkedList<Object[]>();

			// add << aliases to the java stream writers
			data.container.runScriptlet("JavaUtilities.extend_proxy('org.typeexit.kettle.plugin.steps.ruby.streams.StdStreamWriter') {alias << write}\n");
			data.container.runScriptlet("JavaUtilities.extend_proxy('org.typeexit.kettle.plugin.steps.ruby.streams.ErrorStreamWriter') {alias << write}\n");
			data.container.runScriptlet("JavaUtilities.extend_proxy('org.typeexit.kettle.plugin.steps.ruby.streams.StepStreamWriter') {alias << write}\n");

		} catch (Exception e) {
			step.logError("Error Initializing Ruby Scripting Step", e);
			return false;
		}

		return true;
	}

  protected static void setEnv(Map<String, String> newenv)
  {
    try
      {
          Class<?> processEnvironmentClass = Class.forName("java.lang.ProcessEnvironment");
          Field theEnvironmentField = processEnvironmentClass.getDeclaredField("theEnvironment");
          theEnvironmentField.setAccessible(true);
          Map<String, String> env = (Map<String, String>) theEnvironmentField.get(null);
          env.putAll(newenv);
          Field theCaseInsensitiveEnvironmentField = processEnvironmentClass.getDeclaredField("theCaseInsensitiveEnvironment");
          theCaseInsensitiveEnvironmentField.setAccessible(true);
          Map<String, String> cienv = (Map<String, String>)     theCaseInsensitiveEnvironmentField.get(null);
          cienv.putAll(newenv);
      }
      catch (NoSuchFieldException e)
      {
        try {
          Class[] classes = Collections.class.getDeclaredClasses();
          Map<String, String> env = System.getenv();
          for(Class cl : classes) {
              if("java.util.Collections$UnmodifiableMap".equals(cl.getName())) {
                  Field field = cl.getDeclaredField("m");
                  field.setAccessible(true);
                  Object obj = field.get(env);
                  Map<String, String> map = (Map<String, String>) obj;
                  map.clear();
                  map.putAll(newenv);
              }
          }
        } catch (Exception e2) {
          e2.printStackTrace();
        }
      } catch (Exception e1) {
          e1.printStackTrace();
      } 
  }

	private void setGemHome() {

		// if specified directly, take it
		String gemHomeString = step.environmentSubstitute(meta.getGemHome());

		// if not, fall back to RUBY_GEM_HOME
		if (Const.isEmpty(gemHomeString) && !Const.isEmpty(step.getVariable("RUBY_GEM_HOME"))) {
			gemHomeString = step.environmentSubstitute("${RUBY_GEM_HOME}");
		}

		// if that fails, use the standard one
		if (Const.isEmpty(gemHomeString)) {
			gemHomeString = step.getPluginDir() + Const.FILE_SEPARATOR + "gems";
		}

		if (!Const.isEmpty(gemHomeString)) {

			File gemHomePath = new File(gemHomeString);
			gemHomePath = gemHomePath.getAbsoluteFile();

			RubyHash configHash = (RubyHash) data.container.parse("require 'rbconfig'; RbConfig::CONFIG").run();
{'fraction_non_alphanumeric': 0.12165599508815016, 'fraction_numerical': 0.0005262696254714499, 'mean_word_length': 4.355565993424142, 'pattern_counts': {'":': 0, '<': 30, '<?xml version=': 0, '>': 14, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7323923', 'n_tokens_mistral': 3916, 'n_tokens_neox': 3490, 'n_words': 639}