einstein-enum 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f8a9f734114e08c89f7b162fc923d5be76db5fb9
4
+ data.tar.gz: ae6349c335b99e8ac11e0dd13d0fc020412f9e3e
5
+ SHA512:
6
+ metadata.gz: 225faebfed556dc7165fe79b86e90ef09c54454dc74710f3f5dbbe9373fa2d1b8289b9b8897669840dd9b28e0eaa2e78347dec3a0815d2b49b46abf7189ee494
7
+ data.tar.gz: 397d849c770e61564d8f83d092af2b61fdf6e5ee635e54134d4a1b24a651a6d62651a3d2e9fcd94f716e668acfd2b2ac9ccd41bd118ff96b14cd9b2da5ed45f7
data/README.md ADDED
@@ -0,0 +1,96 @@
1
+ # Einstein-enum
2
+
3
+ This is a port of Swift's Enum type. In Swift, Enums have all sorts of great
4
+ features, like associated values and raw values. GENIUS.
5
+
6
+ They are truly awesome, unlike generics, terrible compilation error messages,
7
+ and the rather pedantic `init` method requirements.
8
+
9
+
10
+ # Compatibility
11
+
12
+ I wrote this for use in RubyMotion, but I wrote it in a way that it is
13
+ compatible with all Ruby implementations (that I know of!).
14
+
15
+
16
+ ## Usage
17
+
18
+ ```ruby
19
+ class Api < Enum
20
+ value :Status
21
+ value :Posts, raw_value: :posts
22
+ # these represent an endpoint for "user detail", either by id or username
23
+ value :User, Fixnum
24
+ value :User, String
25
+
26
+ def url
27
+ # inside this method, the constants "Status" and methods "User" are defined
28
+ # to return the appropriate enum values/matchers
29
+ case self
30
+ when Status
31
+ "/status"
32
+ when User(-1) # one off values can be matched!
33
+ "/users/by_role/admin"
34
+ when User(Fixnum)
35
+ # you can access the values using array access
36
+ id = self[0]
37
+ "/users/by_id/#{id}"
38
+ when User(String)
39
+ username = self[0]
40
+ "/users/by_name/#{username}"
41
+ when Posts
42
+ "/posts"
43
+ else
44
+ nil
45
+ end
46
+ end
47
+
48
+ end
49
+
50
+
51
+ status_endpoint = Api.Status
52
+ user_endpoint = Api.User(2) # if you tried `Api.User` you would get a 'value not defined' expection
53
+
54
+
55
+ # matching is really where it's at:
56
+ case status_endpoint
57
+ # simple enum values
58
+ when Api.Status
59
+ when Api.Posts
60
+ # here is the cool "polymorphic" matching. I have not seen any other Ruby Enum
61
+ # gems offer this:
62
+ when Api.User(Fixnum)
63
+ when Api.User(String)
64
+ end
65
+
66
+
67
+ # the ability to have methods associated with the enum values is very handy, and
68
+ # adds object-oriented ideas to the boring old 'enum' type.
69
+ puts status_endpoint.url # => https://api.api.com/api/v1/status
70
+ ```
71
+
72
+ ## Testing
73
+
74
+ ```
75
+ rake spec
76
+ ```
77
+
78
+ ## Todo
79
+
80
+ It would be neat to have "named" values, instead of just positional.
81
+
82
+ ```ruby
83
+ class Api < Enum
84
+ value :User, id: Fixnum
85
+ end
86
+
87
+ user_endpoint = Api.User(id: 2)
88
+
89
+ id = user_endpoint[:id]
90
+ id = user_endpoint.id
91
+ ```
92
+
93
+ Using Einstein-enum, I'd like to build a [Moya][]-like tool for RubyMotion
94
+
95
+
96
+ [Moya]: https://github.com/ashfurrow/Moya
@@ -0,0 +1,7 @@
1
+ if defined?(Motion::Project::Config)
2
+ Motion::Project::App.setup do |app|
3
+ app.files << File.join(File.dirname(__FILE__), 'enum.rb')
4
+ end
5
+ else
6
+ require File.join(File.dirname(__FILE__), 'enum.rb')
7
+ end
data/lib/enum.rb ADDED
@@ -0,0 +1,167 @@
1
+ module EinsteinEnum
2
+
3
+ class Enum
4
+
5
+ class << self
6
+
7
+ def value(name, *types)
8
+ if self == Enum
9
+ raise 'Don\'t add values to Enum'
10
+ end
11
+
12
+ if !name.is_a?(Symbol)
13
+ raise '`name` must be a symbol'
14
+ end
15
+
16
+ opts = {}
17
+ if types.last.is_a?(Hash)
18
+ opts = types.pop
19
+ end
20
+
21
+ new_value = EnumValue.new
22
+ new_value.enum = self
23
+ new_value.name = name
24
+ new_value.types = types
25
+ new_value.raw_value = opts[:raw_value] || (2 ** all_values.count)
26
+
27
+ if existing_value = find(name, types)
28
+ raise "There is already a value (#{existing_value}) that matches #{new_value}"
29
+ end
30
+
31
+ if all_values.any? { |existing_value| existing_value.raw_value == new_value.raw_value }
32
+ raise "There is already a value (#{existing_value}) with the raw_value of `#{new_value.raw_value.inspect}`"
33
+ end
34
+
35
+ all_values << new_value
36
+
37
+ if !self.respond_to?(name)
38
+ define_singleton_method(name) do |*type_values|
39
+ create(name, *type_values)
40
+ end
41
+ if types.count == 0
42
+ self.const_set(name, new_value)
43
+ end
44
+ define_method(name) do |*type_values|
45
+ self.class.create(name, *type_values)
46
+ end
47
+ end
48
+
49
+ new_value
50
+ end
51
+
52
+ def all_values
53
+ if self == Enum
54
+ return []
55
+ end
56
+
57
+ @all_values ||= [] + self.superclass.all_values
58
+ end
59
+
60
+ def find(name, type_values)
61
+ all_values.find do |enum_value|
62
+ enum_value.name == name && types_match(enum_value.types, type_values)
63
+ end
64
+ end
65
+
66
+ def types_match(types, type_values)
67
+ if types.count != type_values.count
68
+ false
69
+ else
70
+ matches = true
71
+ if type_values.all? { |t| t.is_a?(Class) }
72
+ types.each_with_index do |t, index|
73
+ matches &&= type_values[index].ancestors.include?(t)
74
+ break if not matches
75
+ end
76
+ else
77
+ types.each_with_index do |t, index|
78
+ matches &&= type_values[index].is_a?(t)
79
+ break if not matches
80
+ end
81
+ end
82
+ matches
83
+ end
84
+ end
85
+
86
+ def create(name, *type_values)
87
+ enum_value = find(name, type_values)
88
+ if enum_value.nil?
89
+ raise "could not find value for #{name}(#{type_values.map{|t|t.to_s}.join(', ')})"
90
+ elsif !type_values.empty? && type_values.all? { |t| t.is_a?(Class) }
91
+ return enum_value
92
+ else
93
+ return enum_value.instance(type_values)
94
+ end
95
+ end
96
+
97
+ end
98
+
99
+ attr_accessor :enum_value
100
+ attr_accessor :values
101
+
102
+ def ===(value)
103
+ if value.is_a?(EnumValue)
104
+ value.raw_value == raw_value
105
+ elsif value.is_a?(Enum)
106
+ value.enum_value.name == enum_value.name && value.values.count == values.count && begin
107
+ matches = true
108
+ values.each_with_index do |v, index|
109
+ matches &&= v == value.values[index]
110
+ break if not matches
111
+ end
112
+ matches
113
+ end
114
+ else
115
+ false
116
+ end
117
+ end
118
+
119
+ def raw_value
120
+ enum_value.raw_value
121
+ end
122
+
123
+ def [](index)
124
+ values[index]
125
+ end
126
+
127
+ def to_s
128
+ "#{enum_value.name}(#{values.map{|t|t.to_s}.join(', ')})"
129
+ end
130
+
131
+ end
132
+
133
+
134
+ class EnumValue
135
+ attr_accessor :enum
136
+ attr_accessor :name
137
+ attr_accessor :types
138
+ attr_accessor :raw_value
139
+
140
+ def ===(value)
141
+ if value.is_a?(EnumValue)
142
+ value.raw_value == raw_value
143
+ elsif value.is_a?(Enum)
144
+ value.enum_value.name == name && Enum.types_match(types, value.values)
145
+ else
146
+ false
147
+ end
148
+ end
149
+
150
+ def instance(values)
151
+ instance = enum.new
152
+ instance.enum_value = self
153
+ instance.values = values
154
+ instance
155
+ end
156
+
157
+ def to_s
158
+ "#{name}(#{types.map{|t|t.to_s}.join(', ')})"
159
+ end
160
+
161
+ end
162
+ end
163
+
164
+
165
+ if !defined?(::Enum)
166
+ Enum = EinsteinEnum::Enum
167
+ end
data/lib/version.rb ADDED
@@ -0,0 +1,3 @@
1
+ module EinsteinEnum
2
+ Version = '1.0.0'
3
+ end
@@ -0,0 +1,163 @@
1
+ require "einstein_enum"
2
+
3
+
4
+ ROOT = "https://api.api.com/api/v1"
5
+
6
+
7
+ class Api < Enum
8
+ value :Status
9
+ value :User, Fixnum
10
+ value :User, String
11
+ value :Posts, raw_value: :posts
12
+
13
+ def root
14
+ ROOT
15
+ end
16
+
17
+ def url
18
+ case self
19
+ when Status
20
+ root + "/status"
21
+ when User(Fixnum)
22
+ id = self[0]
23
+ root + "/users/by_id/#{id}"
24
+ when User(String)
25
+ username = self[0]
26
+ root + "/users/by_name/#{username}"
27
+ when Posts
28
+ root + "/posts"
29
+ else
30
+ nil
31
+ end
32
+ end
33
+
34
+ end
35
+
36
+
37
+ # extending an Enum
38
+ class AnotherApi < Api
39
+ value :PostDetail, Fixnum
40
+
41
+ def url
42
+ case self
43
+ when PostDetail(Fixnum)
44
+ root + "/posts/#{self[0]}"
45
+ else
46
+ super
47
+ end
48
+ end
49
+
50
+ end
51
+
52
+
53
+ status = Api.Status
54
+ user_type = Api.User(Fixnum)
55
+ user_id = Api.User(2)
56
+ user_name = Api.User('colinta')
57
+ posts = Api.Posts
58
+ post_detail = AnotherApi.PostDetail(2)
59
+
60
+
61
+ RSpec.describe EinsteinEnum do
62
+
63
+ describe "matches" do
64
+ it "should match Api.Status" do
65
+ expect(case status
66
+ when Api.Status
67
+ true
68
+ when Api.User(Fixnum)
69
+ false
70
+ else
71
+ false
72
+ end).to(eq(true))
73
+ end
74
+
75
+ it "should match Api.Status" do
76
+ expect(case status
77
+ when Api.Status
78
+ true
79
+ when Api.User(Fixnum)
80
+ false
81
+ else
82
+ false
83
+ end).to eq(true)
84
+ end
85
+
86
+ it "should match Api.User(Fixnum)" do
87
+ expect(case user_id
88
+ when Api.Status
89
+ false
90
+ when Api.User(String)
91
+ false
92
+ when Api.User(Fixnum)
93
+ true
94
+ else
95
+ false
96
+ end).to eq(true)
97
+ end
98
+
99
+ it "should match Api.User(String)" do
100
+ expect(case user_name
101
+ when Api.Status
102
+ false
103
+ when Api.User(Fixnum)
104
+ false
105
+ when Api.User(String)
106
+ true
107
+ else
108
+ false
109
+ end).to eq(true)
110
+ end
111
+
112
+ it "should match Api.User(2)" do
113
+ expected_id = 2
114
+ expect(case user_id
115
+ when Api.Status
116
+ false
117
+ when Api.User(expected_id - 1)
118
+ false
119
+ when Api.User(expected_id)
120
+ true
121
+ else
122
+ false
123
+ end).to eq(true)
124
+ end
125
+ end
126
+
127
+ describe "raw values" do
128
+ it "should be unique" do
129
+ expect(user_type.raw_value).not_to eq(user_type.raw_value)
130
+ expect(user_id.raw_value).not_to eq(user_id.raw_value)
131
+ expect(user_name.raw_value).not_to eq(user_name.raw_value)
132
+ expect(posts.raw_value).not_to eq(posts.raw_value)
133
+ end
134
+
135
+ it "should be customizable" do
136
+ expect(posts.raw_value).to eq(:posts)
137
+ end
138
+ end
139
+
140
+ describe("methods") do
141
+ it "should create the Status url string" do
142
+ expect(status.url).to eq(ROOT + "/status")
143
+ end
144
+
145
+ it "should create the User(Fixnum) url string" do
146
+ expect(user_id.url).to eq(ROOT + "/users/by_id/2")
147
+ end
148
+
149
+ it "should create the User(String) url string" do
150
+ expect(user_name.url).to eq(ROOT + "/users/by_name/colinta")
151
+ end
152
+
153
+ it "should create the Post url string" do
154
+ expect(posts.url).to eq(ROOT + "/posts")
155
+ end
156
+
157
+ it "should create the PostDetail(Fixnum) url string" do
158
+ expect(post_detail.url).to eq(ROOT + "/posts/2")
159
+ end
160
+ end
161
+
162
+ end
163
+
File without changes
metadata ADDED
@@ -0,0 +1,68 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: einstein-enum
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Colin T.A. Gray
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-03-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '2.11'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '2.11'
27
+ description: |
28
+ Let's not kid ourselves: Swift didn't remove much of the tedium of iOS
29
+ development. However, it introduced the best ENUM type that I've ever seen.
30
+ email:
31
+ - colinta@gmail.com
32
+ executables: []
33
+ extensions: []
34
+ extra_rdoc_files: []
35
+ files:
36
+ - README.md
37
+ - lib/einstein-enum.rb
38
+ - lib/enum.rb
39
+ - lib/version.rb
40
+ - spec/einstein_enum_spec.rb
41
+ - spec/spec_helper.rb
42
+ homepage: https://github.com/colinta/einstein-enum
43
+ licenses:
44
+ - BSD
45
+ metadata: {}
46
+ post_install_message:
47
+ rdoc_options: []
48
+ require_paths:
49
+ - lib
50
+ required_ruby_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ required_rubygems_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - '>='
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
60
+ requirements: []
61
+ rubyforge_project:
62
+ rubygems_version: 2.4.5
63
+ signing_key:
64
+ specification_version: 4
65
+ summary: Swift's Enums are genius. Especially in a language that doesn't suck.
66
+ test_files:
67
+ - spec/einstein_enum_spec.rb
68
+ - spec/spec_helper.rb