DSL Powered by Rabbit 2.1.2 DSL Yukio Goto sg.rb 2014/04/29
May 11, 2015
DSL Powered by Rabbit 2.1.2
DSL
Yukio Gotosg.rb
2014/04/29
DSL Powered by Rabbit 2.1.2
Who am I ?
Yukio Gotofavorite
Ruby, emacs, zsh, tennis✓✓
workRakuten Asia Pte. Ltd.✓As senior application engineer✓
✓
✓
DSL Powered by Rabbit 2.1.2
IDs
github
https://github.com/byplayer/
@byplayer
DSL Powered by Rabbit 2.1.2
Today's target
use DSL -*-*-*-*-*-*- making DSL
DSL Powered by Rabbit 2.1.2
DSL
What is DSL ?
DSL Powered by Rabbit 2.1.2
DSL
DSL =Domain Specific
Language
DSL Powered by Rabbit 2.1.2
external DSL
SQL✓
SELECT id, user_name FROM users
DSL Powered by Rabbit 2.1.2
external DSL
configuration files✓
http { passenger_root /usr/local/rvm/gems/ruby-2.1.0/gems/passenger-4.0.29; passenger_ruby /usr/local/rvm/wrappers/ruby-2.1.0/ruby;
include mime.types; default_type application/octet-stream;
# ...}
DSL Powered by Rabbit 2.1.2
internal DSL
Rails✓
class User < ActiveRecord::Base validates :name, presence: trueend
DSL Powered by Rabbit 2.1.2
BTW
By the way
DSL Powered by Rabbit 2.1.2
Did you make DSL ?
Did you make DSL ?
DSL Powered by Rabbit 2.1.2
use only ?
Do you think
you can only use DSL?
DSL Powered by Rabbit 2.1.2
No
NO !!!
DSL Powered by Rabbit 2.1.2
You
You
DSL Powered by Rabbit 2.1.2
can
can
DSL Powered by Rabbit 2.1.2
make
make
DSL Powered by Rabbit 2.1.2
your own DSL
your own DSL
DSL Powered by Rabbit 2.1.2
AND
AND
DSL Powered by Rabbit 2.1.2
Ruby
Rubyis
DSL Powered by Rabbit 2.1.2
easiest language
one of the most
easiest language
DSL Powered by Rabbit 2.1.2
making DSL
making DSL
DSL Powered by Rabbit 2.1.2
How to make DSL
How to make DSL
DSL Powered by Rabbit 2.1.2
before that
Before that
DSL Powered by Rabbit 2.1.2
Benefit of making DSL
DSL makes your Applicationeasy to write✓easy to read✓easy to use✓
✓
DSL Powered by Rabbit 2.1.2
theme
Configuration file using DSL
DSL Powered by Rabbit 2.1.2
config version 1
# singapore-ruby-group.confmeetup do |conf| conf.group = 'Singapore-Ruby-Group' conf.organizer = 'Winston Teo' conf.sponsors = ['Engine Yard', 'Silicon Straits', 'Plug-In@Blk71']end
DSL Powered by Rabbit 2.1.2
how to use
mt = Meetup.load('singapore-ruby-group.conf')
puts mt.group# => Singapore-Ruby-Group
puts mt.organizer# => Winston Teo
puts mt.sponsors.join(', ')# => Engine Yard, Silicon Straits, Plug-In@Blk71
DSL Powered by Rabbit 2.1.2
implementation
# Sample class to load configurationclass Meetup attr_accessor :group, :organizer, :sponsors
def self.load(path) fail "config file not found: #{path}" unless File.exist?(path)
mt = Meetup.new File.open(path, 'r') do |file| mt.instance_eval(File.read(path), path) end
mt end
private
def meetup yield self endend
DSL Powered by Rabbit 2.1.2
key point
def self.load # ... File.open(path, 'r') do |file| mt.instance_eval(File.read(path), path) end # ...end
def meetup yield selfend
DSL Powered by Rabbit 2.1.2
first point
pass string to 'instance_eval'
def self.load # ... File.open(path, 'r') do |file| mt.instance_eval(File.read(path), path) end # ...end
DSL Powered by Rabbit 2.1.2
instance_eval
"instance_eval"
DSL Powered by Rabbit 2.1.2
instance_eval
interpretes String
DSL Powered by Rabbit 2.1.2
instance_eval
as Ruby code
DSL Powered by Rabbit 2.1.2
instance_eval
using Object context
DSL Powered by Rabbit 2.1.2
instance_eval
In this case,
DSL Powered by Rabbit 2.1.2
instance_eval
configuration file is
DSL Powered by Rabbit 2.1.2
instance_eval
interpreted as
DSL Powered by Rabbit 2.1.2
instance_eval
Meetup class source code
DSL Powered by Rabbit 2.1.2
expand instance_eval virtually
def self.load # File.open(path, 'r') do |file| # mt.instance_eval(File.read(path), path) # end meetup do |conf| conf.group = 'Singapore-Ruby-Group' conf.organizer = 'Winston Teo' conf.sponsors = ['Engine Yard', 'Silicon Straits', 'Plug-In@Blk71'] endend
DSL Powered by Rabbit 2.1.2
easy
quite easy
DSL Powered by Rabbit 2.1.2
but
BUT
DSL Powered by Rabbit 2.1.2
Typing config
Typing 'conf.'
DSL Powered by Rabbit 2.1.2
is is
is not
DSL Powered by Rabbit 2.1.2
sexy
sexy
DSL Powered by Rabbit 2.1.2
Is it sexy ?
meetup do |conf| conf.group = 'Singapore-Ruby-Group' conf.organizer = 'Winston Teo' conf.sponsors = ['Engine Yard', 'Silicon Straits', 'Plug-In@Blk71']end
DSL Powered by Rabbit 2.1.2
ideal configuration
# singapore-ruby-group.confmeetup { group 'Singapore-Ruby-Group' organizer 'Winston Teo' sponsors ['Engine Yard', 'Silicon Straits', 'Plug-In@Blk71']}
DSL Powered by Rabbit 2.1.2
readable
readable✓
DRY✓
DSL Powered by Rabbit 2.1.2
Let's use
Let's use
DSL Powered by Rabbit 2.1.2
Ruby's black magic
Ruby's black magic
DSL Powered by Rabbit 2.1.2
parsing
parsing it
DSL Powered by Rabbit 2.1.2
load
class Meetup attr_accessor :group, :organizer, :sponsors
def self.load(path) mt = new File.open(path, 'r') do |file| loader = MeetupLoader.new(mt) loader.instance_eval(file.read, path) end
mt endend
DSL Powered by Rabbit 2.1.2
make support class
make support class
MeetupLoader
DSL Powered by Rabbit 2.1.2
make support class
define setter method
DSL Powered by Rabbit 2.1.2
make support class
not use '='
DSL Powered by Rabbit 2.1.2
make support class
and
DSL Powered by Rabbit 2.1.2
make support class
load function(meetup) only
DSL Powered by Rabbit 2.1.2
make support class (setter)
class MeetupLoader def initialize(mt) @meetup = mt end # ... def group(g) @meetup.group = g end
def organizer(o) @meetup.organizer = o end # ...end
DSL Powered by Rabbit 2.1.2
instance_eval again
class MeetupLoader # ... def meetup(&block) instance_eval(&block) end # ...end
DSL Powered by Rabbit 2.1.2
The point of black magic
instance_eval again
DSL Powered by Rabbit 2.1.2
instance_eval for block
instance_eval with block
DSL Powered by Rabbit 2.1.2
changes
changes
DSL Powered by Rabbit 2.1.2
default receiver
default receiver
DSL Powered by Rabbit 2.1.2
in the block
in the block
DSL Powered by Rabbit 2.1.2
original code
class Meetup attr_accessor :group, :organizer, :sponsors
def self.load(path) mt = new File.open(path, 'r') do |file| loader = MeetupLoader.new(mt) loader.instance_eval(file.read, path) end
mt endend
DSL Powered by Rabbit 2.1.2
expand instance_eval virtually 1
def self.load(path) mt = new File.open(path, 'r') do |file| loader = MeetupLoader.new(mt) # loader.instance_eval(file.read, path) loader.meetup { group 'Singapore-Ruby-Group' organizer 'Winston Teo' # ... } end #...
DSL Powered by Rabbit 2.1.2
instance_eval with block
class MeetupLoader # ... def meetup(&block) instance_eval(&block) end # ...end
DSL Powered by Rabbit 2.1.2
expand instance_eval virtually 2
def meetup(&block) # instance_eval(&block) #{ self.group 'Singapore-Ruby-Group' self.organizer 'Winston Teo' # ... # }end
DSL Powered by Rabbit 2.1.2
The trick
The trick
DSL Powered by Rabbit 2.1.2
of
of
DSL Powered by Rabbit 2.1.2
Ruby's black magic
Ruby's black magic
DSL Powered by Rabbit 2.1.2
is
is
DSL Powered by Rabbit 2.1.2
instance_eval with block
instance_eval with block
DSL Powered by Rabbit 2.1.2
expand instance_eval virtually 2
def meetup(&block) # instance_eval(&block) #{ self.group 'Singapore-Ruby-Group' self.organizer 'Winston Teo' # ... # }end
DSL Powered by Rabbit 2.1.2
Today
Today,
DSL Powered by Rabbit 2.1.2
I
I
DSL Powered by Rabbit 2.1.2
don't
don't
DSL Powered by Rabbit 2.1.2
talk about
talk about
DSL Powered by Rabbit 2.1.2
caution points
caution points
DSL Powered by Rabbit 2.1.2
caution points
security✓
handle method_missing✓handle syntax error✓
DSL Powered by Rabbit 2.1.2
source code
https://github.com/byplayer/meetup_config✓https://github.com/byplayer/meetup_config_ex
✓
DSL Powered by Rabbit 2.1.2
take a way
You can make DSL✓
instance_eval is interesting✓
DSL Powered by Rabbit 2.1.2
Hiring
Rakuten Asia Pte. Ltd. wants to hire
Server side Application Engineer (Ruby, java)
✓
iOS, Android Application Engineer
✓
✓
DSL Powered by Rabbit 2.1.2
Any question ?
Any question ?
DSL Powered by Rabbit 2.1.2
Thank you
Thank youfor listening