Slide 1

Slide 1 text

We know you have a choice in conference tracks. Thank you for choosing ours. Welcome to the Fringe Ruby track.

Slide 2

Slide 2 text

My Little C Extension: Lego Robots are Magic!

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

Brandon Hays. Catsultant. @tehviking.

Slide 7

Slide 7 text

<3

Slide 8

Slide 8 text

About C Extensions?

Slide 9

Slide 9 text

About Robots?

Slide 10

Slide 10 text

About Ponies?

Slide 11

Slide 11 text

Actually a story about BURNOUT

Slide 12

Slide 12 text

Crazy passionate about software

Slide 13

Slide 13 text

Programming: SUPER FUN

Slide 14

Slide 14 text

Programming: SUPER FUN

Slide 15

Slide 15 text

Started running out of steam

Slide 16

Slide 16 text

Because fun would be unprofessional.

Slide 17

Slide 17 text

Lazy? Incapable? Broken?

Slide 18

Slide 18 text

I FEAR these things about myself, but I don’t BELIEVE them.

Slide 19

Slide 19 text

So... What was wrong with me?

Slide 20

Slide 20 text

How can you combat these demons?

Slide 21

Slide 21 text

More work?

Slide 22

Slide 22 text

Distractions?

Slide 23

Slide 23 text

Distraction ROI High Not so good Worthless

Slide 24

Slide 24 text

How can you *actually* combat these demons?

Slide 25

Slide 25 text

You STOP and PLAY.

Slide 26

Slide 26 text

You STOP and PLAY. and SHIP.

Slide 27

Slide 27 text

ONE WEIRD TRICK the high clergy of Computerology don’t want you to know...

Slide 28

Slide 28 text

Coin an acronym.

Slide 29

Slide 29 text

HIPS CAFE

Slide 30

Slide 30 text

HIPS CAFE

Slide 31

Slide 31 text

H it burnout I nspiration purchase P laytime S teal knowledge C heat to win A tone for your cheating F ind a practical application E njoy a Coca-Cola™

Slide 32

Slide 32 text

Hit burnout

Slide 33

Slide 33 text

(this means stop)

Slide 34

Slide 34 text

Inspiration purchase

Slide 35

Slide 35 text

COOOOOOL

Slide 36

Slide 36 text

No content

Slide 37

Slide 37 text

No content

Slide 38

Slide 38 text

THESE PEOPLE MAKE SMARTER BRAINS THAN LEGO

Slide 39

Slide 39 text

My stupid dream

Slide 40

Slide 40 text

Playtime!

Slide 41

Slide 41 text

Yak shaving can be fun when it’s a pet yak

Slide 42

Slide 42 text

Let’s write in C

Slide 43

Slide 43 text

Let’s write in C

Slide 44

Slide 44 text

No content

Slide 45

Slide 45 text

Let’s write in C

Slide 46

Slide 46 text

Why can’t I just use Ruby?

Slide 47

Slide 47 text

If you’re doing it right, this is where you get stuck

Slide 48

Slide 48 text

Steal knowledge

Slide 49

Slide 49 text

You must build your own mentorship.

Slide 50

Slide 50 text

Devs: We have a responsibility to the community that nurtured us.

Slide 51

Slide 51 text

@cowboyd my mentor therubyracer his experience

Slide 52

Slide 52 text

Advisor Helper Collaborator Secret Puppeteer

Slide 53

Slide 53 text

My prior experience with C extensions

Slide 54

Slide 54 text

Building a C extension

Slide 55

Slide 55 text

1. Create a gem http://guides.rubygems.org/make-your-own-gem/ $ bundle gem brick_pi - Edit .gemspec - Edit lib/brick_pi/version.rb - Add `gem 'rake-compiler'` to Gemfile

Slide 56

Slide 56 text

2. Lay filesystem groundwork http://guides.rubygems.org/gems-with-extensions/ - Create ext/brick_pi/ - Include C files needed for the robot (tick.h, BrickPi.h) - Create native.c - Create extconf.rb

Slide 57

Slide 57 text

2. Lay filesystem groundwork require "bundler/gem_tasks" require "rake/extensiontask" Rake::ExtensionTask.new("native", eval(File.read("brick_pi.gemspec"))) do |ext| ext.ext_dir = "ext/brick_pi" ext.lib_dir = "lib/brick_pi" ext.source_pattern = "*.{c,h}" end Rakefile http://guides.rubygems.org/gems-with-extensions/

Slide 58

Slide 58 text

2. Lay filesystem groundwork Gem::Specification.new do |spec| spec.name = "brick_pi" spec.version = BrickPi::VERSION ... spec.license = "MIT" spec.require_paths = ["lib", "ext"] spec.extensions = ["ext/brick_pi/extconf.rb"] ... end .gemspec http://guides.rubygems.org/gems-with-extensions/

Slide 59

Slide 59 text

require 'mkmf' find_header 'wiringPi.h' find_library 'wiringPi', 'serialOpen' create_makefile 'brick_pi/native' ext/brick_pi/extconf.rb 2. Lay filesystem groundwork

Slide 60

Slide 60 text

“We go to war with the API we wish we had, not the API we have. - Robert Schuller

Slide 61

Slide 61 text

3. Specify Ruby API # I want this API: BrickPi::Native.brickPiSetup() # In Ruby it’d look like this: module BrickPi module Native def self.brickPiSetup() # do some C stuff here end end end

Slide 62

Slide 62 text

# I want this API: BrickPi::Native.brickPiSetup() # In Ruby it’d look like this: module BrickPi module Native def self.brickPiSetup() # do some C stuff here end end end 3. Specify Ruby API Top namespace (from gem)

Slide 63

Slide 63 text

# I want this API: BrickPi::Native.brickPiSetup() # In Ruby it’d look like this: module BrickPi module Native def self.brickPiSetup() # do some C stuff here end end end 3. Specify Ruby API “Native” namespace

Slide 64

Slide 64 text

# I want this API: BrickPi::Native.brickPiSetup() # In Ruby it’d look like this: module BrickPi module Native def self.brickPiSetup() # do some C stuff here end end end 3. Specify Ruby API Setup method

Slide 65

Slide 65 text

STATUS CODE ZERO

Slide 66

Slide 66 text

4. Write first wrapper #include "ruby.h"; #include "tick.h"; #include "BrickPi.h"; VALUE call_brickPiSetup(VALUE self) { return INT2FIX(BrickPiSetup()); } void Init_native() { VALUE BrickPi = rb_define_module("BrickPi"); VALUE Native = rb_define_module_under(BrickPi, "Native"); rb_define_singleton_method(Native, "brickPiSetup", call_brickPiSetup, 0); } ext/brick_pi/native.c

Slide 67

Slide 67 text

#include "ruby.h"; #include "tick.h"; #include "BrickPi.h"; 4. Write first wrapper #include "ruby.h"; #include "tick.h"; #include "BrickPi.h"; VALUE call_brickPiSetup(VALUE self) { return INT2FIX(BrickPiSetup()); } void Init_native() { VALUE BrickPi = rb_define_module("BrickPi"); VALUE Native = rb_define_module_under(BrickPi, "Native"); rb_define_singleton_method(Native, "brickPiSetup", call_brickPiSetup, 0); } ext/brick_pi/native.c Ruby C libs Include:

Slide 68

Slide 68 text

#include "ruby.h"; #include "tick.h"; #include "BrickPi.h"; 4. Write first wrapper #include "ruby.h"; #include "tick.h"; #include "BrickPi.h"; VALUE call_brickPiSetup(VALUE self) { return INT2FIX(BrickPiSetup()); } void Init_native() { VALUE BrickPi = rb_define_module("BrickPi"); VALUE Native = rb_define_module_under(BrickPi, "Native"); rb_define_singleton_method(Native, "brickPiSetup", call_brickPiSetup, 0); } ext/brick_pi/native.c C dependencies Include:

Slide 69

Slide 69 text

#include "ruby.h"; #include "tick.h"; #include "BrickPi.h"; VALUE call_brickPiSetup(VALUE self) { return INT2FIX(BrickPiSetup()); } void Init_native() { VALUE BrickPi = rb_define_module("BrickPi"); VALUE Native = rb_define_module_under(BrickPi, "Native"); rb_define_singleton_method(Native, "brickPiSetup", call_brickPiSetup, 0); } void Init_native() { 4. Write first wrapper ext/brick_pi/native.c “magic” init_ method

Slide 70

Slide 70 text

#include "ruby.h"; #include "tick.h"; #include "BrickPi.h"; VALUE call_brickPiSetup(VALUE self) { return INT2FIX(BrickPiSetup()); } void Init_native() { VALUE BrickPi = rb_define_module("BrickPi"); VALUE Native = rb_define_module_under(BrickPi, "Native"); rb_define_singleton_method(Native, "brickPiSetup", call_brickPiSetup, 0); } VALUE BrickPi = rb_define_module("BrickPi"); VALUE Native = rb_define_module_under(BrickPi, "Native"); 4. Write first wrapper ext/brick_pi/native.c module BrickPi module BrickPi:Native

Slide 71

Slide 71 text

#include "ruby.h"; #include "tick.h"; #include "BrickPi.h"; VALUE call_brickPiSetup(VALUE self) { return INT2FIX(BrickPiSetup()); } void Init_native() { VALUE BrickPi = rb_define_module("BrickPi"); VALUE Native = rb_define_module_under(BrickPi, "Native"); rb_define_singleton_method(Native, "brickPiSetup", call_brickPiSetup, 0); } rb_define_singleton_method 4. Write first wrapper ext/brick_pi/native.c def self.brickPiSetup()

Slide 72

Slide 72 text

#include "ruby.h"; #include "tick.h"; #include "BrickPi.h"; VALUE call_brickPiSetup(VALUE self) { return INT2FIX(BrickPiSetup()); } void Init_native() { VALUE BrickPi = rb_define_module("BrickPi"); VALUE Native = rb_define_module_under(BrickPi, "Native"); rb_define_singleton_method(Native, "brickPiSetup", call_brickPiSetup, 0); } Native, 4. Write first wrapper ext/brick_pi/native.c defined on

Slide 73

Slide 73 text

#include "ruby.h"; #include "tick.h"; #include "BrickPi.h"; VALUE call_brickPiSetup(VALUE self) { return INT2FIX(BrickPiSetup()); } void Init_native() { VALUE BrickPi = rb_define_module("BrickPi"); VALUE Native = rb_define_module_under(BrickPi, "Native"); rb_define_singleton_method(Native, "brickPiSetup", call_brickPiSetup, 0); } "brickPiSetup" 4. Write first wrapper ext/brick_pi/native.c Ruby method name

Slide 74

Slide 74 text

#include "ruby.h"; #include "tick.h"; #include "BrickPi.h"; VALUE call_brickPiSetup(VALUE self) { return INT2FIX(BrickPiSetup()); } void Init_native() { VALUE BrickPi = rb_define_module("BrickPi"); VALUE Native = rb_define_module_under(BrickPi, "Native"); rb_define_singleton_method(Native, "brickPiSetup", call_brickPiSetup, 0); } VALUE call_brickPiSetup call_brickPiSetup 4. Write first wrapper ext/brick_pi/native.c C helper method

Slide 75

Slide 75 text

#include "ruby.h"; #include "tick.h"; #include "BrickPi.h"; VALUE call_brickPiSetup(VALUE self) { return INT2FIX(BrickPiSetup()); } void Init_native() { VALUE BrickPi = rb_define_module("BrickPi"); VALUE Native = rb_define_module_under(BrickPi, "Native"); rb_define_singleton_method(Native, "brickPiSetup", call_brickPiSetup, 0); } 0 4. Write first wrapper ext/brick_pi/native.c no Ruby args

Slide 76

Slide 76 text

#include "ruby.h"; #include "tick.h"; #include "BrickPi.h"; VALUE call_brickPiSetup(VALUE self) { return INT2FIX(BrickPiSetup()); } void Init_native() { VALUE BrickPi = rb_define_module("BrickPi"); VALUE Native = rb_define_module_under(BrickPi, "Native"); rb_define_singleton_method(Native, "brickPiSetup", call_brickPiSetup, 0); } 4. Write first wrapper VALUE call_brickPiSetup ext/brick_pi/native.c wrapper/helper function

Slide 77

Slide 77 text

#include "ruby.h"; #include "tick.h"; #include "BrickPi.h"; VALUE call_brickPiSetup(VALUE self) { return INT2FIX(BrickPiSetup()); } void Init_native() { VALUE BrickPi = rb_define_module("BrickPi"); VALUE Native = rb_define_module_under(BrickPi, "Native"); rb_define_singleton_method(Native, "brickPiSetup", call_brickPiSetup, 0); } 4. Write first wrapper BrickPiSetup() ext/brick_pi/native.c original C function

Slide 78

Slide 78 text

#include "ruby.h"; #include "tick.h"; #include "BrickPi.h"; VALUE call_brickPiSetup(VALUE self) { return INT2FIX(BrickPiSetup()); } void Init_native() { VALUE BrickPi = rb_define_module("BrickPi"); VALUE Native = rb_define_module_under(BrickPi, "Native"); rb_define_singleton_method(Native, "brickPiSetup", call_brickPiSetup, 0); } 4. Write first wrapper INT2FIX ext/brick_pi/native.c cast C int to Ruby Fixnum

Slide 79

Slide 79 text

#include "ruby.h"; #include "tick.h"; #include "BrickPi.h"; VALUE call_brickPiSetup(VALUE self) { return INT2FIX(BrickPiSetup()); } void Init_native() { VALUE BrickPi = rb_define_module("BrickPi"); VALUE Native = rb_define_module_under(BrickPi, "Native"); rb_define_singleton_method(Native, "brickPiSetup", call_brickPiSetup, 0); } 4. Write first wrapper (VALUE self) { 0 ext/brick_pi/native.c wait, what?

Slide 80

Slide 80 text

Object-oriented C

Slide 81

Slide 81 text

#include "ruby.h"; #include "tick.h"; #include "BrickPi.h"; VALUE call_brickPiSetup(VALUE self) { return INT2FIX(BrickPiSetup()); } void Init_native() { VALUE BrickPi = rb_define_module("BrickPi"); VALUE Native = rb_define_module_under(BrickPi, "Native"); rb_define_singleton_method(Native, "brickPiSetup", call_brickPiSetup, 0); } Object-oriented C VALUE VALUE ext/brick_pi/native.c magic ‘VALUE’ type

Slide 82

Slide 82 text

#include "ruby.h"; #include "tick.h"; #include "BrickPi.h"; VALUE call_brickPiSetup(VALUE self) { return INT2FIX(BrickPiSetup()); } void Init_native() { VALUE BrickPi = rb_define_module("BrickPi"); VALUE Native = rb_define_module_under(BrickPi, "Native"); rb_define_singleton_method(Native, "brickPiSetup", call_brickPiSetup, 0); } Object-oriented C self ext/brick_pi/native.c always pass self as 1st argument

Slide 83

Slide 83 text

4. Build gem & experiment $ rake compile $ cd ~/projects/brick_pi_test $ cat "gem 'brick_pi', '/users/pi/projects/brick_pi'" > Gemfile $ bundle exec irb > require 'brick_pi' > BrickPi::Native.brickPiSetup()

Slide 84

Slide 84 text

No content

Slide 85

Slide 85 text

It’ll probably look more like this

Slide 86

Slide 86 text

Holy crap, it worked > BrickPi::Native.brickPiSetup() => 0 => Also hey nice and cool job you are a pretty good => programmer you should maybe do this for a living

Slide 87

Slide 87 text

But wait, our Ruby file is empty! require "brick_pi/version" require "brick_pi/native" module BrickPi # Your code goes here... end lib/brick_pi.rb

Slide 88

Slide 88 text

So where did that Ruby come from?

Slide 89

Slide 89 text

From here! void Init_native() { VALUE BrickPi = rb_define_module("BrickPi"); VALUE Native = rb_define_module_under(BrickPi, "Native"); rb_define_singleton_method(Native, "brick_pi_setup", call_brick_pi_setup, 0); } ext/brick_pi/native.c

Slide 90

Slide 90 text

So we just wrote Ruby... in C?

Slide 91

Slide 91 text

Of course! How do you think Matz does it?

Slide 92

Slide 92 text

Heck, how do you think Ruby does it?

Slide 93

Slide 93 text

Believe it or not, that was the hard part.

Slide 94

Slide 94 text

The rest: - Flesh out API - Spin the motors

Slide 95

Slide 95 text

Flesh out the API

Slide 96

Slide 96 text

Find what you want to wrap

Slide 97

Slide 97 text

Documentation/ example code

Slide 98

Slide 98 text

MotorSpeed Addresses PORT_X constants MotorEnable Use example code...

Slide 99

Slide 99 text

MotorSpeed Addresses PORT_X constants MotorEnable ...to decipher source code

Slide 100

Slide 100 text

C Extension Style Guide

Slide 101

Slide 101 text

Hard-Won knowledge

Slide 102

Slide 102 text

Is best when won by someone else

Slide 103

Slide 103 text

• Keep low-level Ruby ugly, close to C code as possible • Low-level wrappers in BrickPi::Native module • Prefix C method helpers for easy identification

Slide 104

Slide 104 text

Keep low-level Ruby ugly, close to C code as possible rb_define_singleton_method(Native, "BrickPiUpdateValues", bprb_BrickPiUpdateValues, 0); BrickPi::Native.BrickPiUpdateValues() lib/brick_pi/bot.rb ext/brick_pi/native.c

Slide 105

Slide 105 text

No content

Slide 106

Slide 106 text

Low-level wrappers in BrickPi::Native module void Init_native() { VALUE BrickPi = rb_define_module("BrickPi"); VALUE Native = rb_define_module_under(BrickPi, "Native"); VALUE MotorSpeed = rb_define_module_under(Native, "MotorSpeed"); module BrickPi class Motor def spin(speed) speed = [-100, [100, speed].min].max motor_speed = (speed * 2.55).round Native::MotorSpeed[@port] = motor_speed end end end lib/brick_pi/motor.rb ext/brick_pi/native.c

Slide 107

Slide 107 text

Prefix C method helpers for easy identification VALUE bprb_BrickPiSetup(VALUE self) { return INT2FIX(BrickPiSetup()); } VALUE bprb_BrickPiSetupSensors(VALUE self) { return INT2FIX(BrickPiSetupSensors()); } VALUE bprb_ClearTick(VALUE self) { return INT2FIX(ClearTick()); } VALUE bprb_MotorSpeed_set(VALUE self, VALUE key, VALUE value) { int index = FIX2INT(key); BrickPi.MotorSpeed[index] = FIX2INT(value); return value; } bprb, for brick_pi_ruby

Slide 108

Slide 108 text

Putting it together VALUE bprb_SensorType_set(VALUE self, VALUE key, VALUE value) { int index = FIX2INT(key); BrickPi.SensorType[index] = FIX2INT(value); return value; } ... VALUE SensorType = rb_define_module_under(Native, "SensorType"); rb_define_singleton_method(SensorType, "[]=", bprb_SensorType_set, 2);

Slide 109

Slide 109 text

Putting it together VALUE bprb_SensorType_set(VALUE self, VALUE key, VALUE value) { int index = FIX2INT(key); BrickPi.SensorType[index] = FIX2INT(value); return value; } ... VALUE SensorType = rb_define_module_under(Native, "SensorType"); rb_define_singleton_method(SensorType, "[]=", bprb_SensorType_set, 2); VALUE SensorType = rb_define_module_under(Native, "SensorType"); module Native::SensorType

Slide 110

Slide 110 text

Putting it together VALUE bprb_SensorType_set(VALUE self, VALUE key, VALUE value) { int index = FIX2INT(key); BrickPi.SensorType[index] = FIX2INT(value); return value; } ... VALUE SensorType = rb_define_module_under(Native, "SensorType"); rb_define_singleton_method(SensorType, "[]=", bprb_SensorType_set, 2); rb_define_singleton_method(SensorType, "[]=", def self.[]=(key, val)

Slide 111

Slide 111 text

Putting it together VALUE bprb_SensorType_set(VALUE self, VALUE key, VALUE value) { int index = FIX2INT(key); BrickPi.SensorType[index] = FIX2INT(value); return value; } ... VALUE SensorType = rb_define_module_under(Native, "SensorType"); rb_define_singleton_method(SensorType, "[]=", bprb_SensorType_set, 2); bprb_SensorType_set bprb_SensorType_set call to C helper/wrapper

Slide 112

Slide 112 text

Putting it together VALUE bprb_SensorType_set(VALUE self, VALUE key, VALUE value) { int index = FIX2INT(key); BrickPi.SensorType[index] = FIX2INT(value); return value; } ... VALUE SensorType = rb_define_module_under(Native, "SensorType"); rb_define_singleton_method(SensorType, "[]=", bprb_SensorType_set, 2); VALUE self pass self 1st

Slide 113

Slide 113 text

Putting it together VALUE bprb_SensorType_set(VALUE self, VALUE key, VALUE value) { int index = FIX2INT(key); BrickPi.SensorType[index] = FIX2INT(value); return value; } ... VALUE SensorType = rb_define_module_under(Native, "SensorType"); rb_define_singleton_method(SensorType, "[]=", bprb_SensorType_set, 2); VALUE key, VALUE value ... 2 2 Ruby arguments def self.[]=(key, val)

Slide 114

Slide 114 text

Putting it together VALUE bprb_SensorType_set(VALUE self, VALUE key, VALUE value) { int index = FIX2INT(key); BrickPi.SensorType[index] = FIX2INT(value); return value; } ... VALUE SensorType = rb_define_module_under(Native, "SensorType"); rb_define_singleton_method(SensorType, "[]=", bprb_SensorType_set, 2); { int index = FIX2INT(key); FIX2INT(value) cast Fixnum in Ruby to INT in C

Slide 115

Slide 115 text

Putting it together VALUE bprb_SensorType_set(VALUE self, VALUE key, VALUE value) { int index = FIX2INT(key); BrickPi.SensorType[index] = FIX2INT(value); return value; } ... VALUE SensorType = rb_define_module_under(Native, "SensorType"); rb_define_singleton_method(SensorType, "[]=", bprb_SensorType_set, 2); BrickPi.SensorType[index] = value Set value at position in C array

Slide 116

Slide 116 text

Putting it together VALUE bprb_SensorType_set(VALUE self, VALUE key, VALUE value) { int index = FIX2INT(key); BrickPi.SensorType[index] = FIX2INT(value); return value; } ... VALUE SensorType = rb_define_module_under(Native, "SensorType"); rb_define_singleton_method(SensorType, "[]=", bprb_SensorType_set, 2); return value; return VALUE ‘value’ to Ruby

Slide 117

Slide 117 text

Lather, rinse, repeat

Slide 118

Slide 118 text

void Init_native() { VALUE BrickPi = rb_define_module("BrickPi"); VALUE Native = rb_define_module_under(BrickPi, "Native"); rb_define_singleton_method(Native, "BrickPiSetup", bprb_BrickPiSetup, 0); rb_define_singleton_method(Native, "BrickPiSetupSensors", bprb_BrickPiSetupSensors, 0); rb_define_singleton_method(Native, "ClearTick", bprb_ClearTick, 0); VALUE MotorSpeed = rb_define_module_under(Native, "MotorSpeed"); rb_define_singleton_method(MotorSpeed, "[]=", bprb_MotorSpeed_set, 2); VALUE MotorEnable = rb_define_module_under(Native, "MotorEnable"); rb_define_singleton_method(MotorEnable, "[]=", bprb_MotorEnable_set, 2); VALUE Address = rb_define_module_under(Native, "Address"); rb_define_singleton_method(Address, "[]=", bprb_Address_set, 2); VALUE SensorType = rb_define_module_under(Native, "SensorType"); rb_define_singleton_method(SensorType, "[]=", bprb_SensorType_set, 2); VALUE Sensor = rb_define_module_under(Native, "Sensor"); rb_define_singleton_method(Sensor, "[]", bprb_Sensor_get, 1); VALUE Encoder = rb_define_module_under(Native, "Encoder"); rb_define_singleton_method(Encoder, "[]", bprb_Encoder_get, 1); Does this look repetitive?

Slide 119

Slide 119 text

rb_define_const(Native, "PORT_A", INT2FIX(0)); rb_define_const(Native, "PORT_B", INT2FIX(1)); rb_define_const(Native, "PORT_C", INT2FIX(2)); rb_define_const(Native, "PORT_D", INT2FIX(3)); rb_define_const(Native, "PORT_1", INT2FIX(0)); rb_define_const(Native, "PORT_2", INT2FIX(1)); rb_define_const(Native, "PORT_3", INT2FIX(2)); rb_define_const(Native, "PORT_4", INT2FIX(3)); rb_define_const(Native, "US_PORT", INT2FIX(2)); rb_define_const(Native, "TYPE_SENSOR_TOUCH", INT2FIX(32)); rb_define_const(Native, "TYPE_SENSOR_ULTRASONIC_CONT", INT2FIX(33)); rb_define_const(Native, "TYPE_SENSOR_ULTRASONIC_SS", INT2FIX(34)); rb_define_const(Native, "TYPE_SENSOR_COLOR_FULL", INT2FIX(36)); rb_define_const(Native, "TYPE_SENSOR_COLOR_RED", INT2FIX(37)); rb_define_const(Native, "TYPE_SENSOR_COLOR_GREEN", INT2FIX(38)); rb_define_const(Native, "TYPE_SENSOR_COLOR_BLUE", INT2FIX(39)); rb_define_const(Native, "TYPE_SENSOR_COLOR_NONE", INT2FIX(40)); } That’s because it is repetitive.

Slide 120

Slide 120 text

OK, stop.

Slide 121

Slide 121 text

How do you know all these methods?

Slide 122

Slide 122 text

I didn’t. I cheated.

Slide 123

Slide 123 text

Cheat to win

Slide 124

Slide 124 text

What’s so bad about cheating, anyway?

Slide 125

Slide 125 text

Why should the race always be to the swift, or the Jumble to the quick-witted? Should they be allowed to win merely because of the gifts God gave them? Well I say, "Cheating is the gift man gives himself." - C. Montgomery Burns

Slide 126

Slide 126 text

/via Life Cheating @cheatsoflife

Slide 127

Slide 127 text

C Ext Cheat Sheets http://java.ociweb.com/mark/NFJS/RubyCExtensions.pdf http://blog.jacius.info/ruby-c-extension-cheat-sheet/ https://github.com/ruby/ruby/blob/trunk/include/ruby/ruby.h Pickaxe book

Slide 128

Slide 128 text

Can you tell me all the secrets of C extension methods? Ok, then we can go get tacos.

Slide 129

Slide 129 text

The C is done. Locked in a mysterious black box

Slide 130

Slide 130 text

Ruby API == MVP

Slide 131

Slide 131 text

no. nonono. require 'brick_pi' include BrickPi motor1 = Native::PORT_A motor2 = Native::PORT_B Native.ClearTick() Native.BrickPiSetup() Native::MotorSpeed[motor1] = 200 Native::MotorSpeed[motor2] = -200 Native::Address[0] = 1 Native::Address[1] = 2 Native::MotorEnable[motor1] = 1 Native::MotorEnable[motor2] = 1 Native.Timeout = 3000 Native.BrickPiSetTimeout() Native.BrickPiUpdateValues() pseudo.rb

Slide 132

Slide 132 text

Motors spin! Can haz tacos nao?

Slide 133

Slide 133 text

Better? require 'brick_pi' include BrickPi class BrickBot def start @stop = false ...setup code... Thread.new do until @stop do Native.BrickPiUpdateValues() sleep(50/1000) end end end def stop @stop = true end end pseudo.rb

Slide 134

Slide 134 text

“I have a passion for writing terrible software.”

Slide 135

Slide 135 text

Sorry, no

Slide 136

Slide 136 text

No content

Slide 137

Slide 137 text

module BrickPi class Bot include BrickPi::Configuration attr_accessor :motor_A, :motor_B, :motor_C, :motor_D attr_accessor :sensor_1, :sensor_2, :sensor_3, :sensor_4 def initialize ...same old init code as before... end def start ...same old init code as before... end def stop @stop = true end def run start yield stop end end end

Slide 138

Slide 138 text

# Instantiate the Bot bot = BrickPi.create do |bot| bot.motor :port_B bot.motor :port_C end # Get this party started bot.run do # Half speed on both motors. Max value is 100. bot.motor_B.spin 50 bot.motor_C.spin 50 sleep 5 # Stop motors bot.motor_B.stop bot.motor_C.stop end

Slide 139

Slide 139 text

gem “brick-pi” v. 0.1.0

Slide 140

Slide 140 text

rake  release is a miracle

Slide 141

Slide 141 text

0.1.0 + 45 minutes: remote control

Slide 142

Slide 142 text

Atone for your cheating

Slide 143

Slide 143 text

def tick self.class.devices.each { |d| d.zero } next_behaviors = [] @behaviors.sort.each do |behavior| behavior.apply(next_behaviors) if behavior.active? next_behaviors << behavior end end @behaviors = next_behaviors self.class.properties.each { |p| p.apply self } end def behavior(priority=1, &block) Behavior.new(priority, &block).tap do |behavior| @behaviors << behavior end end def add_behavior(*behaviors) @behaviors.concat behaviors end def stop @stop = true @thread = nil end

Slide 144

Slide 144 text

class BehaviorBot < BrickPi::Bot attr_accessor :speed motor :port_A motor :port_B ultrasonic_sensor :port_3 # A property is always settable and gettable, even while things are running. property :speed_left do |value| @motor_A.spin value end property :speed_right do |value| @motor_B.spin value end def move_forward(seconds) behavior do |time, done| self.speed_left = self.speed_right = speed done.call if time > seconds end end ... end

Slide 145

Slide 145 text

@bot = BehaviorBot.new @bot.speed = 100 @bot.start # move forward for 4 seconds # then turn left for 3 seconds # then move forward for another 10 seconds @bot.move_forward(4).then do @bot.turn_left(3).then do @bot.move_backward(3).then do @bot.stop_motors(nil, true) end end end # Add a behavior to watch sensor and stop if motors are moving @bot.behavior(9) do |time, done| print "SENSOR DISTANCE: " puts @bot.sensor_3.distance if @bot.sensor_3.distance < 10 @bot.speed_left = @bot.speed_right = 0 end end @bot.wait

Slide 146

Slide 146 text

Find a practical application

Slide 147

Slide 147 text

What else can it do?

Slide 148

Slide 148 text

No content

Slide 149

Slide 149 text

What else can it do? # Square 4.times do @bot.forward 2 @bot.turn -90 end

Slide 150

Slide 150 text

What else can it do? # Crazy spirograph 12.times do @bot.forward 2 @bot.turn -140 end

Slide 151

Slide 151 text

What else can it do?

Slide 152

Slide 152 text

Portrait of the artist as a young robot

Slide 153

Slide 153 text

Enjoy an ice-cold Coca-Cola™

Slide 154

Slide 154 text

Conditioning is delicious™

Slide 155

Slide 155 text

Oh no, people are using it.

Slide 156

Slide 156 text

Hit burnout (again)

Slide 157

Slide 157 text

A confession

Slide 158

Slide 158 text

First, a confession That’s my secret, Cap. I’m always burned out.

Slide 159

Slide 159 text

How did I manage to pour dozens of hours into learning C extensions while severely burned out?

Slide 160

Slide 160 text

More importantly, how did that activity actually help alleviate and even fix my burnout?

Slide 161

Slide 161 text

Programming is for humans.

Slide 162

Slide 162 text

You are a human.

Slide 163

Slide 163 text

H it burnout I nspiration purchase P laytime S teal knowledge C heat to win A tone for your cheating F ind a practical application E njoy a Coca-Cola™

Slide 164

Slide 164 text

Play is how you get your <3 back.

Slide 165

Slide 165 text

Play is how you remember your limitless potential.

Slide 166

Slide 166 text

Look at this little guy <3

Slide 167

Slide 167 text

Sharing “play projects” is behind a huge portion of good done in the world.

Slide 168

Slide 168 text

make8bitart.com

Slide 169

Slide 169 text

meatspac.es

Slide 170

Slide 170 text

_why’s poignant guide to ruby

Slide 171

Slide 171 text

The world needs you to stop. and play. and ship.

Slide 172

Slide 172 text

Play is the only power fun enough to inspire the next generation.

Slide 173

Slide 173 text

Thank you. @tehviking

Slide 174

Slide 174 text

Sunset Airplane Landing by dgmiami https://www.flickr.com/photos/dgmiami/ Yak by Chandan Kumar https://www.flickr.com/photos/ck-/ Hello World by Windell Oskay https://www.flickr.com/photos/oskay/ Stuck in the Mud by Jason Rogers https://www.flickr.com/photos/restlessglobetrotter/ Lego porn by Eric https://www.flickr.com/photos/ejpphoto/ Black box by thierry ehrmann https://www.flickr.com/photos/home_of_chaos/ Taco cat by Shannon Des Roches Rosa https://www.flickr.com/photos/shannonrosa/ Number Five is Alive by emilydickinsonridesabmx https://www.flickr.com/photos/emilyrides/ Rainbow Dash at BrickCon by Katie Walker https://www.flickr.com/photos/eilonwy77/ Elvis Diner by Rahel Jaskow https://www.flickr.com/photos/rahel_jaskow/ Mini Fluttershy by Pascal https://www.flickr.com/photos/pasukaru76/ Black Cover for Why's (Poignant) Guide to Ruby by Why the Lucky Stiff https://www.flickr.com/photos/_why/ Image credits