under-os-core 1.4.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.
- checksums.yaml +7 -0
- data/README.md +26 -0
- data/lib/core/json.rb +19 -0
- data/lib/core/numeric.rb +23 -0
- data/lib/core/object.rb +5 -0
- data/lib/core/string.rb +72 -0
- data/lib/under-os-core.rb +40 -0
- data/lib/under_os.rb +5 -0
- data/lib/under_os/app.rb +29 -0
- data/lib/under_os/color.rb +184 -0
- data/lib/under_os/config.rb +10 -0
- data/lib/under_os/delegate.rb +10 -0
- data/lib/under_os/events.rb +85 -0
- data/lib/under_os/file.rb +110 -0
- data/lib/under_os/point.rb +50 -0
- data/lib/under_os/screen.rb +9 -0
- data/lib/under_os/timer.rb +65 -0
- data/spec/core/json_spec.rb +32 -0
- data/spec/core/numeric_spec.rb +37 -0
- data/spec/core/string_spec.rb +101 -0
- data/spec/under_os/color_spec.rb +133 -0
- data/spec/under_os/events_spec.rb +71 -0
- data/spec/under_os/file_spec.rb +98 -0
- data/spec/under_os/point_spec.rb +156 -0
- data/spec/under_os/screen_spec.rb +11 -0
- data/spec/under_os/timer_spec.rb +93 -0
- data/under-os-core.gemspec +20 -0
- metadata +93 -0
@@ -0,0 +1,85 @@
|
|
1
|
+
#
|
2
|
+
# A little events handling concern
|
3
|
+
#
|
4
|
+
module UnderOs::Events
|
5
|
+
|
6
|
+
def on(event, *args, &block)
|
7
|
+
Listeners.add(self, event, *args, block)
|
8
|
+
end
|
9
|
+
|
10
|
+
def off(event)
|
11
|
+
Listeners.remove(self, event)
|
12
|
+
end
|
13
|
+
|
14
|
+
def emit(event, params={})
|
15
|
+
Listeners.kick(self, event, params)
|
16
|
+
end
|
17
|
+
|
18
|
+
module Listeners
|
19
|
+
extend self
|
20
|
+
|
21
|
+
def list(model, event=nil)
|
22
|
+
@listeners ||= Hash.new{|h,k| h[k] = Hash.new{|h,k| h[k] = []} }
|
23
|
+
event ? @listeners[model][event.to_sym] : @listeners[model]
|
24
|
+
end
|
25
|
+
|
26
|
+
def add(model, event, *args, block)
|
27
|
+
list(model, event) << [block, *args]
|
28
|
+
model
|
29
|
+
end
|
30
|
+
|
31
|
+
def all(model, event)
|
32
|
+
list(model, event)
|
33
|
+
end
|
34
|
+
|
35
|
+
def remove(model, event)
|
36
|
+
list(model).delete event.to_sym
|
37
|
+
model
|
38
|
+
end
|
39
|
+
|
40
|
+
def kick(model, event, params)
|
41
|
+
event = Event.new(event, params) unless event.is_a?(Event)
|
42
|
+
|
43
|
+
all(model, event.name).each do |block, method_name, *args|
|
44
|
+
if !block && method_name
|
45
|
+
block = Proc.new{ |e|
|
46
|
+
a = method(method_name).arity == 0 ? [] : [e];
|
47
|
+
__send__(method_name, *a) }
|
48
|
+
context = model
|
49
|
+
elsif block && method_name # <- considering it's a context reference
|
50
|
+
context = method_name
|
51
|
+
else
|
52
|
+
context = nil
|
53
|
+
end
|
54
|
+
|
55
|
+
args = block.arity == 0 ? [] : [event]
|
56
|
+
|
57
|
+
if context
|
58
|
+
context.instance_exec *args, &block
|
59
|
+
else
|
60
|
+
block.call *args
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
model
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
class Event
|
69
|
+
attr_reader :name, :params
|
70
|
+
|
71
|
+
def initialize(name, params)
|
72
|
+
@name = name.to_sym
|
73
|
+
@params = params
|
74
|
+
end
|
75
|
+
|
76
|
+
def method_missing(name, *args, &block)
|
77
|
+
if @params.has_key?(name.to_sym)
|
78
|
+
@params[name.to_sym]
|
79
|
+
else
|
80
|
+
super name, *args, &block
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
class UnderOs::File
|
2
|
+
attr_reader :path, :mode
|
3
|
+
|
4
|
+
def self.open(path, *args, &block)
|
5
|
+
new path, *args, &block
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.read(path, *args)
|
9
|
+
new(path, *args).read
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.write(path, content)
|
13
|
+
new(path, "w"){|f| f.write(content) }
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.size(path)
|
17
|
+
new(path).size
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.delete(path)
|
21
|
+
NSFileManager.defaultManager.removeItemAtPath(UnderOs::File.path(path), error:nil)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.path(path)
|
25
|
+
path.to_s[0] == '/' ? path : NSHomeDirectory().stringByAppendingPathComponent(path)
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.tmp(filename, mode="w", &block)
|
29
|
+
new NSTemporaryDirectory().stringByAppendingPathComponent(filename), mode, &block
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.exists?(path, is_dir=nil)
|
33
|
+
NSFileManager.defaultManager.fileExistsAtPath(UnderOs::File.path(path), isDirectory:is_dir)
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.file?(path)
|
37
|
+
exists?(path, false)
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.dir?(path)
|
41
|
+
exists?(path, true)
|
42
|
+
end
|
43
|
+
|
44
|
+
def initialize(path, mode="r", &block)
|
45
|
+
@path = UnderOs::File.path(path)
|
46
|
+
@mode = mode
|
47
|
+
|
48
|
+
yield(self) if block_given?
|
49
|
+
end
|
50
|
+
|
51
|
+
def url
|
52
|
+
@url ||= NSURL.fileURLWithPath(@path)
|
53
|
+
end
|
54
|
+
|
55
|
+
def read(size=nil)
|
56
|
+
open
|
57
|
+
data = if size == nil
|
58
|
+
@handle.readDataToEndOfFile
|
59
|
+
else
|
60
|
+
@handle.readDataOfLength(size)
|
61
|
+
end
|
62
|
+
close
|
63
|
+
|
64
|
+
@mode == "b" ? data : NSString.alloc.initWithData(data, encoding:NSUTF8StringEncoding)
|
65
|
+
end
|
66
|
+
|
67
|
+
def write(content)
|
68
|
+
if ! UnderOs::File.exists?(@path)
|
69
|
+
NSFileManager.defaultManager.createFileAtPath @path, contents:nil, attributes:nil
|
70
|
+
end
|
71
|
+
|
72
|
+
content = content.to_data('utf-8') if content.is_a?(String)
|
73
|
+
|
74
|
+
open
|
75
|
+
@handle.writeData content
|
76
|
+
close
|
77
|
+
end
|
78
|
+
|
79
|
+
def open
|
80
|
+
@handle = if @mode == "r"
|
81
|
+
NSFileHandle.fileHandleForReadingAtPath(@path)
|
82
|
+
else
|
83
|
+
NSFileHandle.fileHandleForWritingAtPath(@path)
|
84
|
+
end
|
85
|
+
|
86
|
+
@handle.seekToEndOfFile if ["w+", "a"].include?(@mode)
|
87
|
+
end
|
88
|
+
|
89
|
+
def close
|
90
|
+
@handle.closeFile
|
91
|
+
@handle = nil
|
92
|
+
end
|
93
|
+
|
94
|
+
def seek(offset)
|
95
|
+
@handle.seekToFileOffset(offset)
|
96
|
+
end
|
97
|
+
|
98
|
+
def rewind
|
99
|
+
seek 0
|
100
|
+
end
|
101
|
+
|
102
|
+
def size
|
103
|
+
NSFileManager.defaultManager.attributesOfItemAtPath(@path, error: nil).fileSize
|
104
|
+
end
|
105
|
+
|
106
|
+
def truncate(size)
|
107
|
+
@handle.truncateFileAtOffset(size)
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
#
|
2
|
+
# Generic Point/Size unit
|
3
|
+
#
|
4
|
+
class UnderOs::Point
|
5
|
+
attr_accessor :x, :y
|
6
|
+
|
7
|
+
def initialize(x, y=nil)
|
8
|
+
if x.is_a?(UnderOs::Point)
|
9
|
+
y = x.y if x.y
|
10
|
+
x = x.x
|
11
|
+
elsif x.is_a?(Hash)
|
12
|
+
y = x[:y] || x['y'] || nil
|
13
|
+
x = x[:x] || x['x'] || nil
|
14
|
+
end
|
15
|
+
|
16
|
+
@x = x
|
17
|
+
@y = y
|
18
|
+
end
|
19
|
+
|
20
|
+
def ==(*args)
|
21
|
+
point = UnderOs::Point.new(*args) # normalizing
|
22
|
+
x == point.x && y == point.y
|
23
|
+
end
|
24
|
+
|
25
|
+
def *(multiplier)
|
26
|
+
UnderOs::Point.new(x: x * multiplier, y: y * multiplier)
|
27
|
+
end
|
28
|
+
|
29
|
+
def /(divider)
|
30
|
+
UnderOs::Point.new(x: x / divider.to_f, y: y / divider.to_f)
|
31
|
+
end
|
32
|
+
|
33
|
+
def -(point)
|
34
|
+
point = point.is_a?(Numeric) ? UnderOs::Point.new(x: point, y: point) : UnderOs::Point.new(point)
|
35
|
+
UnderOs::Point.new(x: x - point.x, y: y - point.y)
|
36
|
+
end
|
37
|
+
|
38
|
+
def +(point)
|
39
|
+
point = point.is_a?(Numeric) ? UnderOs::Point.new(x: point, y: point) : UnderOs::Point.new(point)
|
40
|
+
UnderOs::Point.new(x: x + point.x, y: y + point.y)
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_s
|
44
|
+
"x=#{x} y=#{y}"
|
45
|
+
end
|
46
|
+
|
47
|
+
def inspect
|
48
|
+
"#<#{self.class.name}:0x#{__id__.to_s(16)} #{to_s}>"
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
class UnderOs::Timer
|
2
|
+
def self.in(duration, options={}, &block)
|
3
|
+
duration = Duration.new(duration) if duration.is_a?(Numeric)
|
4
|
+
new duration.to_f, options.merge(repeat: false), &block
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.every(duration, options={}, &block)
|
8
|
+
duration = Duration.new(duration) if duration.is_a?(Numeric)
|
9
|
+
new duration.to_f, options.merge(repeat: true), &block
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :block, :counter, :interval, :repeats, :_
|
13
|
+
|
14
|
+
def initialize(seconds, options={}, &block)
|
15
|
+
@block = block
|
16
|
+
@counter = options[:repeat].to_i if options[:repeat].is_a?(Numeric)
|
17
|
+
@interval = seconds
|
18
|
+
@repeats = options[:repeat] != false
|
19
|
+
|
20
|
+
@_ = NSTimer.scheduledTimerWithTimeInterval @interval,
|
21
|
+
target: self, selector: :kick, userInfo: nil, repeats: @repeats
|
22
|
+
end
|
23
|
+
|
24
|
+
def stop
|
25
|
+
@_.invalidate
|
26
|
+
self
|
27
|
+
end
|
28
|
+
|
29
|
+
def kick
|
30
|
+
@block.call
|
31
|
+
stop if @counter && (@counter -= 1) <= 0
|
32
|
+
end
|
33
|
+
|
34
|
+
class Duration
|
35
|
+
attr_reader :seconds
|
36
|
+
|
37
|
+
def initialize(seconds)
|
38
|
+
@seconds = seconds.to_f
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_f
|
42
|
+
@seconds
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_i
|
46
|
+
to_f.to_i
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_s
|
50
|
+
"#{to_f} seconds"
|
51
|
+
end
|
52
|
+
|
53
|
+
def ==(duration)
|
54
|
+
duration.is_a?(UnderOs::Timer::Duration) && duration.seconds == @seconds
|
55
|
+
end
|
56
|
+
|
57
|
+
def later(options={}, &block)
|
58
|
+
UnderOs::Timer.in self, options={}, &block
|
59
|
+
end
|
60
|
+
|
61
|
+
def repeat(options={}, &block)
|
62
|
+
UnderOs::Timer.every self, options={}, &block
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
describe JSON do
|
2
|
+
describe ".parse" do
|
3
|
+
it "parses correct json strings" do
|
4
|
+
JSON.parse('{"a":1,"b":true,"c":["data"]}').should == {
|
5
|
+
"a" => 1, "b" => true, "c" => ["data"]
|
6
|
+
}
|
7
|
+
end
|
8
|
+
|
9
|
+
it "raises an exception on malformed data" do
|
10
|
+
-> {
|
11
|
+
JSON.parse("malformed")
|
12
|
+
}.should.raise(JSON::Malformed)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe ".generate" do
|
17
|
+
it "generates JSON string out of objects" do
|
18
|
+
JSON.generate({a:1, b:true, c:[:data]}).should ==
|
19
|
+
'{"a":1,"b":true,"c":["data"]}'
|
20
|
+
end
|
21
|
+
|
22
|
+
it "generates pretty print when asked" do
|
23
|
+
JSON.generate({a:1, b:true, c:[:data]}, :pretty).should ==
|
24
|
+
"{\n \"a\" : 1,\n \"b\" : true,\n \"c\" : [\n \"data\"\n ]\n}"
|
25
|
+
end
|
26
|
+
|
27
|
+
it "allows to export things directly of objects" do
|
28
|
+
{a:1, b:true, c:[:data]}.to_json.should ==
|
29
|
+
'{"a":1,"b":true,"c":["data"]}'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
describe Numeric do
|
2
|
+
|
3
|
+
describe '#milliseconds' do
|
4
|
+
it "returns timer duration in milliseconds" do
|
5
|
+
10.milliseconds.should == UnderOs::Timer::Duration.new(0.01)
|
6
|
+
end
|
7
|
+
|
8
|
+
it "has the 'ms' alias" do
|
9
|
+
10.milliseconds.should == 10.ms
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe '#seconds' do
|
14
|
+
it "returns the time duration in seconds" do
|
15
|
+
10.seconds.should == UnderOs::Timer::Duration.new(10)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '#minutes' do
|
20
|
+
it "returns the time duration in minutes" do
|
21
|
+
10.minutes.should == UnderOs::Timer::Duration.new(10 * 60)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe '#hours' do
|
26
|
+
it "returns the time duration in hours" do
|
27
|
+
10.hours.should == UnderOs::Timer::Duration.new(10 * 60 * 60)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe '#days' do
|
32
|
+
it "returns the time duration in days" do
|
33
|
+
10.days.should == UnderOs::Timer::Duration.new(10 * 60 * 60 * 24)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
describe String do
|
2
|
+
describe '#underscore' do
|
3
|
+
it "converts camelized strings into underscored" do
|
4
|
+
"SomeStuff".underscore.should == 'some_stuff'
|
5
|
+
end
|
6
|
+
|
7
|
+
it "converts dashed strings into underscored as well" do
|
8
|
+
"some-stuff".underscore.should == "some_stuff"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "#camelize" do
|
13
|
+
it "converts underscored strings into camelized" do
|
14
|
+
"some_stuff".camelize.should == "someStuff"
|
15
|
+
end
|
16
|
+
|
17
|
+
it "converts dashed strings into camelized as well" do
|
18
|
+
"some-stuff".camelize.should == "someStuff"
|
19
|
+
end
|
20
|
+
|
21
|
+
it "handles prefixes correctly" do
|
22
|
+
"_some_stuff".camelize.should == "SomeStuff"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '#dasherize' do
|
27
|
+
it "converts camelized strings into dashed" do
|
28
|
+
"SomeStuff".dasherize.should == "some-stuff"
|
29
|
+
end
|
30
|
+
|
31
|
+
it "converts underscored strings into dashed as well" do
|
32
|
+
"some_stuff".dasherize.should == "some-stuff"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '#capitalize' do
|
37
|
+
it "capitalizes strings" do
|
38
|
+
"some stuff".capitalize.should == "Some stuff"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe '#starts_with?' do
|
43
|
+
it "returns true if a string starts with the substring" do
|
44
|
+
"some stuff".starts_with?("some").should == true
|
45
|
+
end
|
46
|
+
|
47
|
+
it "returns false if the string doesn't start with the substring" do
|
48
|
+
"some stuff".starts_with?("non matching").should == false
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe '#ends_with?' do
|
53
|
+
it "returns true if the string ends with the given substring" do
|
54
|
+
"some stuff".ends_with?("stuff").should == true
|
55
|
+
end
|
56
|
+
|
57
|
+
it "returns false if the string doesn't end with the substring" do
|
58
|
+
"some stuff".ends_with?("non matching").should == false
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe '#blank?' do
|
63
|
+
it "returns true if the string is empty" do
|
64
|
+
''.blank?.should == true
|
65
|
+
end
|
66
|
+
|
67
|
+
it "returns true if the string has only white spaces" do
|
68
|
+
" \n\t\r ".blank?.should == true
|
69
|
+
end
|
70
|
+
|
71
|
+
it "returns false if the string has non-space chars in it" do
|
72
|
+
" a ".blank?.should == false
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe '#constantize' do
|
77
|
+
it "returns an object from it's value" do
|
78
|
+
"UnderOs::App".constantize.should == UnderOs::App
|
79
|
+
end
|
80
|
+
|
81
|
+
it "raises an error if the constant is missing" do
|
82
|
+
-> {
|
83
|
+
"Something::Totally::Bogus".constantize
|
84
|
+
}.should.raise(NameError)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
describe '#to_data' do
|
89
|
+
before do
|
90
|
+
@data = "ohai there".to_data
|
91
|
+
end
|
92
|
+
|
93
|
+
it "converts the string into a data object" do
|
94
|
+
@data.is_a?(NSData).should == true
|
95
|
+
end
|
96
|
+
|
97
|
+
it "converts it into data with correct content" do
|
98
|
+
@data.to_s.should == "ohai there"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|