motion-set 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +90 -0
- data/lib/motion-set.rb +10 -0
- data/lib/project/motion-set.rb +173 -0
- metadata +65 -0
data/README.md
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
# Implementation of Ruby `set` module
|
2
|
+
|
3
|
+
This gems implements `Set` class from `set` standard library
|
4
|
+
of Ruby 1.9.3. See [Ruby 1.9.3 `Set` API documentation](
|
5
|
+
http://ruby-doc.org/stdlib-1.9.3/libdoc/set/rdoc/Set.html#method-i-flatten-21)
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
gem 'motion-set'
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install motion-set
|
20
|
+
|
21
|
+
## Implementation details
|
22
|
+
|
23
|
+
### No `require`
|
24
|
+
|
25
|
+
RubyMotion does not support `require`. Instead, when you
|
26
|
+
install this gem, `Set` class will be globally available.
|
27
|
+
|
28
|
+
### `Set` is `NSMutableSet`
|
29
|
+
|
30
|
+
`Set` is implemented as an alias to `NSMutableSet`, which
|
31
|
+
it turn is extended to accomodate all Ruby set methods.
|
32
|
+
This is similar to how other RubyMotion data types
|
33
|
+
(like `Array`) are implemented.
|
34
|
+
|
35
|
+
Advantages of this approach are:
|
36
|
+
|
37
|
+
1. Interoperability with native APIs:
|
38
|
+
* You can pass `Set` objects to APIs expecting `NSSet`
|
39
|
+
or `NSMutableSet`.
|
40
|
+
* Whenever a native API returns an `NSMutableSet` you
|
41
|
+
can treat it as Ruby set.
|
42
|
+
2. There's no performance penalty, because there is no
|
43
|
+
unnecessary indirection.
|
44
|
+
|
45
|
+
However, there are some things you need to note:
|
46
|
+
|
47
|
+
### No `nil` in sets
|
48
|
+
|
49
|
+
`NSMutableSet` does not support adding `nil` object,
|
50
|
+
which applies to your Ruby sets.
|
51
|
+
|
52
|
+
This could fail in subtle ways, for example if you try
|
53
|
+
to call `#map!` with a block which returns `nil`.
|
54
|
+
|
55
|
+
> This could have been avoided if the implementation
|
56
|
+
> checked when `nil` is added, and instead stored a
|
57
|
+
> private singelton sentinel fake nil object, which would
|
58
|
+
> be converted back to `nil` when necessary. However,
|
59
|
+
> this would create a challenge for interoperability
|
60
|
+
> with native APIs: what should this object represent
|
61
|
+
> in Objective-C world?
|
62
|
+
>
|
63
|
+
> If you have opinion on this, you are welcom to
|
64
|
+
> [comment on this issue](
|
65
|
+
> https://github.com/robocat/motion-set/issues/1).
|
66
|
+
|
67
|
+
### No `#divide` method
|
68
|
+
|
69
|
+
[`#divide`](
|
70
|
+
http://ruby-doc.org/stdlib-1.9.3/libdoc/set/rdoc/Set.html#method-i-divide)
|
71
|
+
method is not implemented. You are welcome to contribute.
|
72
|
+
|
73
|
+
### No `SortedSet`
|
74
|
+
|
75
|
+
[`SortedSet`](
|
76
|
+
http://ruby-doc.org/stdlib-1.9.3/libdoc/set/rdoc/SortedSet.html)
|
77
|
+
is not implemented. You are welcome to contribute.
|
78
|
+
|
79
|
+
### No Android support
|
80
|
+
|
81
|
+
There is no Android support, but you are welcome to
|
82
|
+
contribute.
|
83
|
+
|
84
|
+
## Contributing
|
85
|
+
|
86
|
+
1. Fork it
|
87
|
+
2. Create your feature branch: `git checkout -b my-new-feature`
|
88
|
+
3. Commit your changes: `git commit -am 'Add some feature'`
|
89
|
+
4. Push to the branch: `git push origin my-new-feature`
|
90
|
+
5. Create new Pull Request
|
data/lib/motion-set.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
unless defined?(Motion::Project::Config)
|
4
|
+
raise "This file must be required within a RubyMotion project Rakefile."
|
5
|
+
end
|
6
|
+
|
7
|
+
lib_dir_path = File.dirname(File.expand_path(__FILE__))
|
8
|
+
Motion::Project::App.setup do |app|
|
9
|
+
app.files.unshift(Dir.glob(File.join(lib_dir_path, "project/**/*.rb")))
|
10
|
+
end
|
@@ -0,0 +1,173 @@
|
|
1
|
+
Set = NSMutableSet
|
2
|
+
|
3
|
+
class NSMutableSet
|
4
|
+
include Enumerable
|
5
|
+
|
6
|
+
def self.new(enum=nil)
|
7
|
+
if enum
|
8
|
+
enum.to_set
|
9
|
+
else
|
10
|
+
alloc.init
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.[](*ary)
|
15
|
+
new.tap { |set| ary.each { |item| set.add item } }
|
16
|
+
end
|
17
|
+
|
18
|
+
def inspect
|
19
|
+
"#<Set: {#{self.to_a.join ', '}}>"
|
20
|
+
end
|
21
|
+
|
22
|
+
def intersection(enum); send_to_copy(:'intersectSet', enum) end
|
23
|
+
alias_method :&, :intersection
|
24
|
+
|
25
|
+
def union(enum); send_to_copy(:'unionSet:', enum) end
|
26
|
+
alias_method :|, :union
|
27
|
+
alias_method :+, :union
|
28
|
+
|
29
|
+
def difference(enum); send_to_copy(:'minusSet:', enum) end
|
30
|
+
alias_method :-, :difference
|
31
|
+
|
32
|
+
# Why can't you just: alias_method :add, :'addObject:'
|
33
|
+
def add(o); addObject o end
|
34
|
+
alias_method :<<, :add
|
35
|
+
|
36
|
+
def ^(enum)
|
37
|
+
((self | enum) - (self & enum))
|
38
|
+
end
|
39
|
+
|
40
|
+
def add?(o)
|
41
|
+
(include? o) ? nil : (add o)
|
42
|
+
end
|
43
|
+
|
44
|
+
def classify(&block)
|
45
|
+
return to_enum(__method__) unless block_given?
|
46
|
+
reduce({}) do |hash, item|
|
47
|
+
classifier = block.call item
|
48
|
+
hash[classifier] ||= Set[]
|
49
|
+
hash[classifier].add item
|
50
|
+
hash
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
alias_method :clear, :removeAllObjects
|
55
|
+
|
56
|
+
def collect!(&block)
|
57
|
+
return to_enum(__method__) unless block_given?
|
58
|
+
replace collect(&block)
|
59
|
+
end
|
60
|
+
alias_method :map!, :collect!
|
61
|
+
|
62
|
+
# Why can't you just: alias_method :delete, :'removeObject:'
|
63
|
+
def delete(o); removeObject o end
|
64
|
+
|
65
|
+
def delete?(o)
|
66
|
+
(include? o) ? (delete o) : nil
|
67
|
+
end
|
68
|
+
|
69
|
+
def keep_if(&block)
|
70
|
+
return to_enum(__method__) unless block_given?
|
71
|
+
each { |item| delete item unless block.call item }
|
72
|
+
self
|
73
|
+
end
|
74
|
+
|
75
|
+
def delete_if(&block)
|
76
|
+
return to_enum(__method__) unless block_given?
|
77
|
+
each { |item| delete item if block.call item }
|
78
|
+
self
|
79
|
+
end
|
80
|
+
|
81
|
+
# Why can't you just: alias_method :length, :count
|
82
|
+
def length; count end
|
83
|
+
alias_method :size, :length
|
84
|
+
|
85
|
+
def empty?; count == 0 end
|
86
|
+
|
87
|
+
def flatten
|
88
|
+
self.to_enum(:flat_each).to_set
|
89
|
+
end
|
90
|
+
|
91
|
+
def flatten!
|
92
|
+
copy = self.to_set
|
93
|
+
# Not sure why self.to_enum does not work in this case:
|
94
|
+
replace copy.to_enum :flat_each
|
95
|
+
(copy == self) ? nil : self
|
96
|
+
end
|
97
|
+
|
98
|
+
alias_method :include?, :containsObject
|
99
|
+
alias_method :member?, :containsObject
|
100
|
+
|
101
|
+
def merge(enum)
|
102
|
+
enum.each { |item| add item }
|
103
|
+
self
|
104
|
+
end
|
105
|
+
|
106
|
+
def replace(enum)
|
107
|
+
clear
|
108
|
+
merge(enum)
|
109
|
+
end
|
110
|
+
|
111
|
+
def subtract(enum)
|
112
|
+
enum.each { |item| delete item }
|
113
|
+
self
|
114
|
+
end
|
115
|
+
|
116
|
+
def subset?(set)
|
117
|
+
enforce_set(set) && isSubsetOfSet(set)
|
118
|
+
end
|
119
|
+
|
120
|
+
def superset?(set)
|
121
|
+
enforce_set(set) && set.isSubsetOfSet(self)
|
122
|
+
end
|
123
|
+
|
124
|
+
def proper_subset?(set)
|
125
|
+
(subset? set) && (self != set)
|
126
|
+
end
|
127
|
+
|
128
|
+
def proper_superset?(set)
|
129
|
+
(superset? set) && (self != set)
|
130
|
+
end
|
131
|
+
|
132
|
+
def reject!(&block)
|
133
|
+
return to_enum(__method__) unless block_given?
|
134
|
+
each_returning_self_or_nil { |item| delete item if block.call item }
|
135
|
+
end
|
136
|
+
|
137
|
+
def select!(&block)
|
138
|
+
return to_enum(__method__) unless block_given?
|
139
|
+
each_returning_self_or_nil { |item| delete item unless block.call item }
|
140
|
+
end
|
141
|
+
|
142
|
+
protected
|
143
|
+
|
144
|
+
def flat_each(&block)
|
145
|
+
each do |item|
|
146
|
+
(item.instance_of? self.class) ? item.flat_each(&block) : block.(item)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
private
|
151
|
+
|
152
|
+
def each_returning_self_or_nil(&block)
|
153
|
+
old_size = self.size
|
154
|
+
each(&block)
|
155
|
+
(old_size != size) ? self : nil
|
156
|
+
end
|
157
|
+
|
158
|
+
def enforce_set(set)
|
159
|
+
raise ArgumentError, 'value must be a set' unless set.is_a? self.class
|
160
|
+
true
|
161
|
+
end
|
162
|
+
|
163
|
+
def send_to_copy(symbol, enum)
|
164
|
+
self.to_set.tap { |set| set.send(symbol, enum.to_set) }
|
165
|
+
end
|
166
|
+
|
167
|
+
end
|
168
|
+
|
169
|
+
class Enumerable
|
170
|
+
def to_set
|
171
|
+
Set[*self]
|
172
|
+
end
|
173
|
+
end
|
metadata
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: motion-set
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Vladimir Keleshev
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-10-16 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rake
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
description: Set implementation for RubyMotion
|
31
|
+
email:
|
32
|
+
- vladimir@keleshev.com
|
33
|
+
executables: []
|
34
|
+
extensions: []
|
35
|
+
extra_rdoc_files: []
|
36
|
+
files:
|
37
|
+
- README.md
|
38
|
+
- lib/motion-set.rb
|
39
|
+
- lib/project/motion-set.rb
|
40
|
+
homepage: ''
|
41
|
+
licenses:
|
42
|
+
- ''
|
43
|
+
post_install_message:
|
44
|
+
rdoc_options: []
|
45
|
+
require_paths:
|
46
|
+
- lib
|
47
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
48
|
+
none: false
|
49
|
+
requirements:
|
50
|
+
- - ! '>='
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '0'
|
53
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
54
|
+
none: false
|
55
|
+
requirements:
|
56
|
+
- - ! '>='
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: '0'
|
59
|
+
requirements: []
|
60
|
+
rubyforge_project:
|
61
|
+
rubygems_version: 1.8.23
|
62
|
+
signing_key:
|
63
|
+
specification_version: 3
|
64
|
+
summary: Set implementation for RubyMotion
|
65
|
+
test_files: []
|