# Delorean Delorean is a simple functional scripting language. It is used at [PENNYMAC][] as a scripting language for a financial modeling system. ![](http://i.imgur.com/qiG7Av6.jpg) ## Installation $ gem install delorean_lang Or add it to your `Gemfile`, etc. ## Usage require 'delorean_lang' engine = Delorean::Engine.new("MyModule") my_code =<= teen_min && age <= teen_max IndiaInfo: USInfo teen_min = 10 In this example, node `USInfo` provides a definition of a `is_teenager` when provided with an `age` parameter. Node `IndiaInfo` is derived from `USInfo` and so it shares all of its attribute definitions. However, the `teen_min` attribute has been overridden. This specifies that the computation of `is_teenager` will use the newly defined `teen_min`. Therefore, `IndiaInfo.is_teenager` with input of `age = 10` will evaluate to `true`. Whereas, `USInfo.is_teenager` with input of `age = 10` will evaluate to `false`. ### Debugging You can use `(ERR())` to add a breakpoint: ``` USInfo: age = ? teen_max = 19 teen_min = 13 is_teenager = (ERR()) && age >= teen_min && age <= teen_max ``` Then you can call attributes by using their mangled name (e.g. attr__D) and passing the context. attr__D(_e). Of course, you can use `ls` to list available methods. ``` teen_max__D(_e) # 19 age__D(_e) ``` ### TODO TODO: provide details on the following topics: * Supported data types * Data structures (arrays and hashes) * List comprehension * Built-in functions * Defining Delorean-callable class functions * External modules ## Implementation This implementation of Delorean "compiles" script code to Ruby. ### Calling ruby methods from Delorean There are two ways of calling ruby code from delorean. First one is to whitelist methods: ```ruby ::Delorean::Ruby.whitelist.add_method :length do |method| method.called_on String method.called_on Enumerable end ::Delorean::Ruby.whitelist.add_method :first do |method| method.called_on Enumerable, with: [Integer] end ::Delorean::Ruby.whitelist.add_class_method :last do |method| method.called_on ActiveRecord::Base, with: [Integer] end ``` By default Delorean has some methods whitelisted, such as `length`, `min`, `max`, etc. Those can be found in `/lib/delorean/ruby/whitelists/default`. If you don't want to use defaults, you can override whitelist with and empty one. ```ruby require 'delorean/ruby/whitelists/empty' ::Delorean::Ruby.whitelist = ::Delorean::Ruby::Whitelists::Empty.new ``` Another way is to define methods using `delorean_fn` and `cached_delorean_fn`. Use `extend Delorean::Functions` or `include Delorean::Model` in your module or class. ```ruby class Dummy < ActiveRecord::Base include Delorean::Model delorean_fn(:heres_my_number, sig: [0, Float::INFINITY]) do |*a| a.inject(0, :+) end end module DummyModule extend Delorean::Functions delorean_fn(:heres_my_number, sig: [0, Float::INFINITY]) do |*a| a.inject(0, :+) end end ``` `heres_my_number` method will be accessible from Delorean code. ```ruby ExampleScript: a = Dummy.heres_my_number(867, 5309)' b = DummyModule.heres_my_number(867, 5309)' ``` ### Caching Delorean provides `cached_delorean_function` method that will cache result based on arguments. ```ruby cached_delorean_fn :returns_cached_openstruct, sig: 1 do |timestamp| User.all end ``` If `::Delorean::Cache.adapter.cache_item?(...)` returns `false` then caching will not be performed. By default cache keeps the last 1000 of the results per class. You can override it: ```ruby ::Delorean::Cache.adapter = ::Delorean::Cache::Adapters::RubyCache.new(size_per_class: 10) ``` If you want use other caching method, you can use your own adapter: ```ruby ::Delorean::Cache.adapter = ::My::Custom::Cache::Adapter.new ``` Delorean expects it to have methods with following signatures: ```ruby cache_item(klass:, cache_key:, item:) fetch_item(klass:, cache_key:, default:) cache_key(klass:, method_name:, args:) clear!(klass:) clear_all! cache_item?(klass:, method_name:, args:) # See lib/delorean/cache/adapters/base.rb ``` TODO: provide details ## License Delorean has been released under the MIT license. Please check the [LICENSE][] file for more details. [license]: https://github.com/rubygems/rubygems.org/blob/master/MIT-LICENSE [pennymac]: http://www.pennymacusa.com [functional programming]: http://en.wikipedia.org/wiki/Functional_programming