-
Notifications
You must be signed in to change notification settings - Fork 21
/
bruno_sutic_ruby_struct.rb
118 lines (85 loc) · 2.89 KB
/
bruno_sutic_ruby_struct.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# Ruby Struct class
# Using bare Struct {{{
# Struct returns a class (!!)
struct = Struct.new(:id, :name) # => #<Class:0x007f89cc003950>
struct.is_a? Class # => true
# Instantiating a new struct class
instance = struct.new(123, "John Wayne") # => #<struct id=123, name="John Wayne">
instance.id # => 123
instance.name # => "John Wayne"
instance.name = "Clint Eastwood" # => "Clint Eastwood"
instance.name # => "Clint Eastwood"
instance[:name] = "Charles Bronson" # => "Charles Bronson"
instance.name # => "Charles Bronson"
# Thoughts:
# - yea.. but why?
# - instantiating twice is un-Ruby
# }}}
# Subclassing struct {{{
# ** Ruby idiom **
class Cowboy < Struct.new(:id, :name)
def says
"My id is more than #{id}" # => "My id is more than Infinity"
end
end
chuck = Cowboy.new(234, "Chuck Norris") # => #<struct Cowboy id=234, name="Chuck Norris">
chuck.id = Float::INFINITY # => Infinity
chuck.says # => "My id is more than Infinity"
# the equivalent class
class Cowboy2
attr_accessor :id, :name # => nil
def initialize(id, name)
@id = id
@name = name
end
def says
"My id is more than #{id}"
end
end
# Ok.. but still, why? Arguments:
# - elegant and succinct, accessor defined in a single place
# - struct states *nothing* happens in an initializer
# - faster than writing the initializer
# - it's an idiom you'll see in many, many gems
# - using it feels good, try it
# }}}
# Better usage with a constant {{{
class Gun < Struct.new(:name, :size)
# ...
end
# Downside: anonymous class in ancestor chain
Gun.ancestors # => [Gun, #<Class:0x007f89cc001920>, Struct, Enumerable, Object, JSON::Ext::Generator::GeneratorMethods::Object, Kernel, BasicObject]
# Solution:
Pistol = Struct.new(:bullets) do # => Struct
def shoot
"bang"
end
end # => Pistol
Pistol.ancestors # => [Pistol, Struct, Enumerable, Object, JSON::Ext::Generator::GeneratorMethods::Object, Kernel, BasicObject]
# What really happens there?
struct = Struct.new(:foo) do # => Struct
def something
end
end # => #<Class:0x007f89cc000520>
struct.is_a? Class # => true
struct.name # => nil
# Interesting (assignment happens on the object on the right)
Something = struct # => Something
struct.name # => "Something"
# }}}
# Is it really better? {{{
# Is using better syntax really better?
Rifle = Struct.new(:model) do # => Struct
def shoot
"ka-bang"
end
end # => Rifle
# Downsides:
# - a lot of devs don't know what it is
# - editor issues (ctags doesn't recognize new class)
# - not pretty, defining methods in a block?
# Suggestion:
# - "improved" syntax for ruby gems
# - "classic" syntax for your application code
# }}}
# vim: fdm=marker