under-os 1.0.0 → 1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5fd9a1ae79f101e53c35741c6935b5eca5353da6
4
- data.tar.gz: 44943f17a76499bd0bb3b3fc80f5b52cb0ea5c5d
3
+ metadata.gz: 5ff1789b7dfe17082f36cf9eea7c8c3444077716
4
+ data.tar.gz: 520c56f69fdc975f55be7e49d87f3742c12e4dca
5
5
  SHA512:
6
- metadata.gz: 1ac6a73d2fe135c0df1cce46b4b20880dbd4a826f857fa25a308b6d4d62f3465369a446ef7c92c98b5253129bf9f52379bcfd59a246a8c6a0da53170581008d3
7
- data.tar.gz: 27607c0bf2e284d6cc39cd06b45e6f71913329dbcbc14b1225b29f731c0af8755d2bd75ec8bc166e2ef6765e3643d406de9c258f67d37e5dffbaeb69691f84b5
6
+ metadata.gz: ea4aab7ec336bd41cde899ab86f4caeaa9d206cb7261915eb85157b97a117b628b41e204df63627ac9e18f36e99e0cedc70f876d7441a6cee27bfbe74afb2aef
7
+ data.tar.gz: 3e7eee7f55f17c5952b96741f2db01cd7ea38e2842476eca515f27a3e093cb8c5516bef93f8dd51cfbd86be60c398d1f1e861540ed63e6283e4183ed41ff9e2c
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ language: objective-c
2
+ install: bundle install
3
+ script: bundle exec rake spec
data/Gemfile CHANGED
@@ -1,2 +1,3 @@
1
1
  source 'https://rubygems.org'
2
+
2
3
  gemspec
data/Gemfile.lock CHANGED
@@ -1,16 +1,18 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- under-os (0.0.0)
4
+ under-os (1.0.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
+ motion-facon (0.5.0.1)
9
10
  rake (10.1.0)
10
11
 
11
12
  PLATFORMS
12
13
  ruby
13
14
 
14
15
  DEPENDENCIES
16
+ motion-facon
15
17
  rake
16
18
  under-os!
data/README.md CHANGED
@@ -1,11 +1,11 @@
1
- # Under OS
1
+ # Under OS [![Build Status](https://travis-ci.org/under-os/under-os.png)](https://travis-ci.org/under-os/under-os) [![Code Climate](https://codeclimate.com/github/under-os/under-os.png)](https://codeclimate.com/github/under-os/under-os)
2
2
 
3
- An experiment to build a thin web-like wrapper over iOS using rubymotion
3
+ [UnderOS](http://under-os.com) is an experiment in building a thin web-like wrapper over iOS using rubymotion
4
4
 
5
- The idea is to use incapsulation instead of monkey patching and build the
5
+ The idea is to use encapsulation instead of monkey patching and build the
6
6
  entire development platform correctly and from scratch.
7
7
 
8
- The result should be a webbish like ifrastructure that uses ruby instead
8
+ The result should be a _webbish_ infrastructure that uses ruby instead
9
9
  of JavaScript and compiles into native code in the end.
10
10
 
11
11
  That's gonna be legendary!
@@ -37,14 +37,6 @@ Now, Run it!
37
37
  cd test && rake
38
38
  ```
39
39
 
40
- ## What's What
41
-
42
- * `app/layouts` - where you keep your HTML layouts
43
- * `app/pages` - where the page scripts live
44
- * `app/styles` - where the CSS files go
45
- * `app/models` - where your data models chill
46
- * `app/views` - where you keep your custom views/components classes
47
-
48
40
 
49
41
  # Copyright & License
50
42
 
data/Rakefile CHANGED
@@ -5,6 +5,7 @@ require 'bundler'
5
5
  Bundler.require
6
6
 
7
7
  require 'under-os'
8
+ require 'motion-facon' if ARGV[0] == 'spec'
8
9
 
9
10
  Motion::Project::App.setup do |app|
10
11
  app.name = 'uos-demo'
@@ -1,14 +1,13 @@
1
1
  <page title="Home">
2
2
  <view id="buttons">
3
- <button id="b1" data-page="stuff">Various Stuff</button>
4
- <button id="b2" data-page="calculator">Calculator Demo</button>
5
- <button id="b3" data-page="inputs">Inputs Demo</button>
6
- <button id="b4" data-page="sidebars">Sidebar Demo</button>
7
- <button id="b5" data-page="scrolls">Scrolls Demo</button>
8
- <button id="b6" data-page="navbar">Navbar Demo</button>
9
- <button id="b7" data-page="alerts">Alerts Demo</button>
10
- <button id="b8" data-page="lockers">Lockers Demo</button>
11
- <button id="b9" data-page="http">HTTP Demo</button>
12
- <button id="b10" data-page="collections">Collections</button>
3
+ <button id="b1" data-page="calculator">Calculator Demo</button>
4
+ <button id="b2" data-page="inputs">Inputs Demo</button>
5
+ <button id="b3" data-page="sidebars">Sidebar Demo</button>
6
+ <button id="b4" data-page="scrolls">Scrolls Demo</button>
7
+ <button id="b5" data-page="navbar">Navbar Demo</button>
8
+ <button id="b6" data-page="alerts">Alerts Demo</button>
9
+ <button id="b7" data-page="lockers">Lockers Demo</button>
10
+ <button id="b8" data-page="http">HTTP Demo</button>
11
+ <button id="b9" data-page="collections">Collections</button>
13
12
  </view>
14
13
  </page>
@@ -5,5 +5,11 @@ class CollectionsPage < UnderOs::Page
5
5
  @collection.on :item do |item, index|
6
6
  item.children[0].text = "##{index + 1}"
7
7
  end
8
+ @collection.on :select do |item|
9
+ item.addClass 'selected'
10
+ end
11
+ @collection.on :unselect do |item|
12
+ item.removeClass 'selected'
13
+ end
8
14
  end
9
15
  end
@@ -13,7 +13,11 @@ class HttpPage < UnderOs::Page
13
13
  @locker.show
14
14
 
15
15
  UnderOs::HTTP.get search_url do |response|
16
- @result.load parse_first_image_url(response.body) do
16
+ if image_url = parse_first_image_url(response)
17
+ @result.load image_url do
18
+ @locker.hide
19
+ end
20
+ else
17
21
  @locker.hide
18
22
  end
19
23
  end
@@ -22,12 +26,13 @@ class HttpPage < UnderOs::Page
22
26
  def search_url
23
27
  query = @search.value
24
28
  query = 'puppy' if query.empty?
29
+ query = String.new(query).url_encode
25
30
 
26
- "https://www.google.com.au/search?q=#{query}&source=lnms&tbm=isch"
31
+ "https://ajax.googleapis.com/ajax/services/search/images?v=1.0&q=#{query}"
27
32
  end
28
33
 
29
- def parse_first_image_url(html)
30
- html.scan(/imgurl=(http:\/\/[^&]+)/)[0][0]
34
+ def parse_first_image_url(response)
35
+ response.json["responseData"]["results"][0]["url"] rescue nil # ftw!
31
36
  end
32
37
 
33
38
  end
@@ -17,3 +17,7 @@ collection item label {
17
17
  border-radius: 2px;
18
18
  text-align: center;
19
19
  }
20
+
21
+ collection item.selected label {
22
+ background: yellow;
23
+ }
data/lib/under-os.rb CHANGED
@@ -8,16 +8,14 @@ Motion::Project::App.instance_eval do
8
8
  Dir.glob(File.dirname(__FILE__) + '/**/*.rb').reverse.each do |file|
9
9
  app.files.insert(0, file) if file != __FILE__
10
10
  end
11
- end
12
11
 
13
- setup_before_under_os *args do |app|
14
12
  app.resources_dirs << File.dirname(__FILE__) + "/assets"
15
13
  app.resources_dirs << "app/styles/" if File.exists?("app/styles")
16
14
  app.resources_dirs << "app/layouts/" if File.exists?("app/layouts")
17
15
 
18
16
  app.fonts << "fontawesome-webfont.ttf"
19
-
20
- instance_exec app, &block
21
17
  end
18
+
19
+ setup_before_under_os *args, &block
22
20
  end
23
21
  end
@@ -94,26 +94,28 @@ class UnderOs::Color
94
94
 
95
95
  def color_from_string(name)
96
96
  name = name.downcase.strip
97
- name = 'clear' if name == 'transparent'
98
- name = 'darkGray' if name == 'darkgray'
99
- name = 'lightGray' if name == 'lightgray'
97
+ name = ALIASES[name] || name
100
98
 
101
99
  return UIColor.send("#{name}Color") if IOS_COLORS.include?(name)
102
100
 
103
101
  name = HTML_COLORS[name] || name
104
102
  name = "#" + name[1]*2 + name[2]*2 + name[3]*2 if name =~ /^#[abcdef0-9]{3}$/
105
103
 
106
- r, g, b, a = if name =~ /^#[abcdef0-9]{6}$/
107
- hex_2_rgb(name)
108
- elsif m = name.match(/^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d*)\s*\)$/)
104
+ r, g, b, a = color_2_rgba(name)
105
+
106
+ color_from_array(r, g, b, a || 1.0)
107
+ end
108
+
109
+ def color_2_rgba(color)
110
+ if color =~ /^#[abcdef0-9]{6}$/
111
+ hex_2_rgb(color)
112
+ elsif m = color.match(/^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d*)\s*\)$/)
109
113
  [m[1].to_i, m[2].to_i, m[3].to_i]
110
- elsif m = name.match(/^rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d*)\s*,\s*([\d\.]+)\s*\)$/)
114
+ elsif m = color.match(/^rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d*)\s*,\s*([\d\.]+)\s*\)$/)
111
115
  [m[1].to_i, m[2].to_i, m[3].to_i, m[4].to_f]
112
116
  else
113
- raise "Malformed color spec: #{name.inspect}"
117
+ raise "Malformed color spec: #{color.inspect}"
114
118
  end
115
-
116
- color_from_array(r, g, b, a || 1.0)
117
119
  end
118
120
 
119
121
  def color_from_param(param) # 0.0..1.0
@@ -144,6 +146,12 @@ class UnderOs::Color
144
146
  end
145
147
  end
146
148
 
149
+ ALIASES = {
150
+ 'transparent' => 'clear',
151
+ 'darkgray' => 'darkGray',
152
+ 'lightgray' => 'lightGray'
153
+ }
154
+
147
155
  IOS_COLORS = %w[
148
156
  black
149
157
  darkGray
@@ -0,0 +1,5 @@
1
+ class Array
2
+ def to_query(key)
3
+ collect { |value| value.to_query("#{key}[]") }.join '&'
4
+ end
5
+ end
@@ -0,0 +1,10 @@
1
+ class Hash
2
+ #
3
+ # Converts a hash into an URL query string
4
+ #
5
+ def to_query(namespace=nil)
6
+ map do |key, value|
7
+ value.to_query(namespace ? "#{namespace}[#{key}]" : key)
8
+ end.sort * '&'
9
+ end
10
+ end
@@ -0,0 +1,19 @@
1
+ class JSON
2
+ class Malformed < StandardError ; end
3
+
4
+ def self.parse(string, encoding=nil)
5
+ error = Pointer.new(:object)
6
+ data = NSJSONSerialization.JSONObjectWithData string.to_data(encoding), options:0, error:error
7
+
8
+ if error[0]
9
+ raise Malformed
10
+ else
11
+ data
12
+ end
13
+ end
14
+
15
+ def self.generate(object, pretty=false)
16
+ options = pretty ? NSJSONWritingPrettyPrinted : 0
17
+ NSJSONSerialization.dataWithJSONObject(object, options:options, error:nil).to_s
18
+ end
19
+ end
@@ -0,0 +1,9 @@
1
+ class Object
2
+ def to_query(key)
3
+ "#{key.to_s.url_encode}=#{to_s.url_encode}"
4
+ end
5
+
6
+ def to_json(pretty=nil)
7
+ JSON.generate(self, pretty)
8
+ end
9
+ end
@@ -37,4 +37,44 @@ class String
37
37
  end
38
38
  constant
39
39
  end
40
+
41
+ def url_encode(encoding=nil)
42
+ stringByAddingPercentEscapesUsingEncoding ENCODINGS[encoding] || NSUTF8StringEncoding
43
+ end
44
+
45
+ def url_decode(encoding=nil)
46
+ stringByReplacingPercentEscapesUsingEncoding ENCODINGS[encoding] || NSUTF8StringEncoding
47
+ end
48
+
49
+ def to_data(encoding=nil)
50
+ dataUsingEncoding ENCODINGS[encoding] || NSUTF8StringEncoding
51
+ end
52
+
53
+ ENCODINGS = {
54
+ 'utf-8' => NSUTF8StringEncoding,
55
+ 'utf-16' => NSUTF16StringEncoding,
56
+ 'utf-32' => NSUTF32StringEncoding,
57
+ 'ascii' => NSASCIIStringEncoding,
58
+ 'latin1' => NSISOLatin1StringEncoding,
59
+ 'latin2' => NSISOLatin2StringEncoding,
60
+ 'cp1250' => NSWindowsCP1250StringEncoding,
61
+ 'cp1251' => NSWindowsCP1251StringEncoding,
62
+ 'cp1252' => NSWindowsCP1252StringEncoding,
63
+ 'cp1253' => NSWindowsCP1253StringEncoding,
64
+ 'cp1254' => NSWindowsCP1254StringEncoding,
65
+
66
+
67
+ # NSNEXTSTEPStringEncoding = 2,
68
+ # NSJapaneseEUCStringEncoding = 3,
69
+ # NSSymbolStringEncoding = 6,
70
+ # NSNonLossyASCIIStringEncoding = 7,
71
+ # NSShiftJISStringEncoding = 8,
72
+ # NSISO2022JPStringEncoding = 21,
73
+ # NSMacOSRomanStringEncoding = 30,
74
+ # NSUTF16BigEndianStringEncoding = 0x90000100,
75
+ # NSUTF16LittleEndianStringEncoding = 0x94000100,
76
+ # NSUTF32BigEndianStringEncoding = 0x98000100,
77
+ # NSUTF32LittleEndianStringEncoding = 0x9c000100,
78
+ # NSProprietaryStringEncoding = 65536
79
+ }
40
80
  end
data/lib/under_os/file.rb CHANGED
@@ -69,7 +69,7 @@ class UnderOs::File
69
69
  NSFileManager.defaultManager.createFileAtPath @path, contents:nil, attributes:nil
70
70
  end
71
71
 
72
- content = content.dataUsingEncoding(NSUTF8StringEncoding) if content.is_a?(String)
72
+ content = content.to_data('utf-8') if content.is_a?(String)
73
73
 
74
74
  open
75
75
  @handle.writeData content
@@ -0,0 +1,30 @@
1
+ #
2
+ # A simple cookie jar thing
3
+ #
4
+ class UnderOs::HTTP::Cookies
5
+ def initialize(cookies, url)
6
+ @url = url.is_a?(String) ? NSURL.URLWithString(url) : url
7
+ @hash = cookies
8
+ end
9
+
10
+ def headers
11
+ NSHTTPCookie.requestHeaderFieldsWithCookies(ios_cookies)
12
+ end
13
+
14
+ def to_s
15
+ headers["Cookie"]
16
+ end
17
+
18
+ protected
19
+
20
+ def ios_cookies
21
+ @hash.map do |key, value|
22
+ NSHTTPCookie.cookieWithProperties({
23
+ NSHTTPCookieDomain => @url.host,
24
+ NSHTTPCookiePath => "/",
25
+ NSHTTPCookieName => key.to_s,
26
+ NSHTTPCookieValue => value.to_s
27
+ })
28
+ end
29
+ end
30
+ end
@@ -1,9 +1,11 @@
1
1
  #
2
2
  # Options:
3
3
  #
4
- # * :method - GET, POST, ...
5
- # * :cookies - Hash of cookie values
6
- # * :stream - boolean, in case you want to stream stuff
4
+ # * :method - GET, POST, ...
5
+ # * :cookies - Hash of cookie values
6
+ # * :stream - boolean, in case you want to stream stuff
7
+ # * :params - the POST/PUT/PATCH params hash
8
+ # * :encoding - the POST data encoding
7
9
  #
8
10
  # Events:
9
11
  #
@@ -37,10 +39,14 @@ class UnderOs::HTTP::Request
37
39
  @receiver = Receiver.new(self, @options[:stream])
38
40
  @connection = NSURLConnection.alloc.initWithRequest(@request, delegate:@receiver)
39
41
  @connection.start
42
+
43
+ return self
40
44
  end
41
45
 
42
46
  def cancel
43
47
  @connection.cancel
48
+
49
+ return self
44
50
  end
45
51
 
46
52
  def method
@@ -58,32 +64,43 @@ class UnderOs::HTTP::Request
58
64
  @options[:cookies] || {}
59
65
  end
60
66
 
67
+ def params
68
+ @options[:params] || {}
69
+ end
70
+
71
+ def encoding
72
+ @options[:encoding] || 'utf-8'
73
+ end
74
+
61
75
  protected
62
76
 
63
77
  def build_request
64
- url = NSURL.URLWithString(NSString.stringWithFormat(@url))
65
- NSMutableURLRequest.requestWithURL(url).tap do |request|
66
- request.setHTTPMethod self.method
67
- #request.setHTTPBody self.params
68
-
69
- cookies = self.cookies.map do |key, value|
70
- NSHTTPCookie.cookieWithProperties({
71
- NSHTTPCookieDomain => url.host,
72
- NSHTTPCookiePath => "/",
73
- NSHTTPCookieName => key.to_s,
74
- NSHTTPCookieValue => value.to_s
75
- })
76
- end
78
+ query = params.to_query
79
+ url = build_url(query)
77
80
 
78
- request.setAllHTTPHeaderFields NSHTTPCookie.requestHeaderFieldsWithCookies(cookies) if cookies.size > 0
81
+ NSMutableURLRequest.requestWithURL(url).tap do |request|
82
+ request.setHTTPMethod method
79
83
 
80
- headers.each do |key, value|
84
+ headers.merge(UnderOs::HTTP::Cookies.new(cookies, url).headers).each do |key, value|
81
85
  request.addValue value, forHTTPHeaderField:key
82
86
  end
83
87
 
84
- # [newRequest addValue:@"application/x-www-form-urlencoded;charset=UTF-8" forHTTPHeaderField:@"Content-Type"];
85
- # [newRequest setHTTPBody:[query dataUsingEncoding:NSUTF8StringEncoding]];
88
+ if %w[POST PUT PATCH DELETE].include?(method) && !query.empty?
89
+ request.addValue "application/x-www-form-urlencoded;charset=#{encoding}", forHTTPHeaderField:"Content-Type"
90
+ request.setHTTPBody query.to_data(encoding)
91
+ end
86
92
  end
87
93
  end
94
+
95
+ def build_url(query)
96
+ url = @url
97
+
98
+ if method == "GET" && !query.empty?
99
+ url += url.include?('?') ? '&' : '?'
100
+ url += query
101
+ end
102
+
103
+ NSURL.URLWithString(url)
104
+ end
88
105
  end
89
106
 
@@ -14,6 +14,10 @@ class UnderOs::HTTP::Response
14
14
  @body ||= @data.to_s
15
15
  end
16
16
 
17
+ def json
18
+ JSON.parse(body)
19
+ end
20
+
17
21
  def content_length
18
22
  headers["Content-Length"].to_i
19
23
  end
data/lib/under_os/http.rb CHANGED
@@ -1,5 +1,31 @@
1
1
  class UnderOs::HTTP
2
2
  def self.get(url, options={}, &block)
3
- Request.new(url, options.merge(method: :get), &block).send
3
+ request url, options.merge(method: :get), &block
4
+ end
5
+
6
+ def self.post(url, options={}, &block)
7
+ request url, options.merge(method: :post), &block
8
+ end
9
+
10
+ def self.put(url, options={}, &block)
11
+ request url, options.merge(method: :put), &block
12
+ end
13
+
14
+ def self.patch(url, options={}, &block)
15
+ request url, options.merge(method: :patch), &block
16
+ end
17
+
18
+ def self.head(url, options={}, &block)
19
+ request url, options.merge(method: :head), &block
20
+ end
21
+
22
+ def self.delete(url, options={}, &block)
23
+ request url, options.merge(method: :delete), &block
24
+ end
25
+
26
+ protected
27
+
28
+ def self.request(*args, &block)
29
+ Request.new(*args, &block).send
4
30
  end
5
31
  end
@@ -59,10 +59,12 @@ class UnderOs::UI::Collection::Delegate < UIViewController
59
59
  ################################################################
60
60
 
61
61
  def collectionView(collection, didSelectItemAtIndexPath: indexPath)
62
- @collection.emit(:select, index: indexPath.row, section: indexPath.section)
62
+ item = collection.cellForItemAtIndexPath(indexPath).uos_view_for(@collection)
63
+ @collection.emit(:select, item: item, index: indexPath.row, section: indexPath.section)
63
64
  end
64
65
 
65
66
  def collectionView(collection, didDeselectItemAtIndexPath: indexPath)
66
- @collection.emit(:unselect, index: indexPath.row, section: indexPath.section)
67
+ item = collection.cellForItemAtIndexPath(indexPath).uos_view_for(@collection)
68
+ @collection.emit(:unselect, item: item, index: indexPath.row, section: indexPath.section)
67
69
  end
68
70
  end
@@ -14,7 +14,7 @@ class UnderOs::UI::Collection < UnderOs::UI::View
14
14
 
15
15
  def on(*args, &block)
16
16
  super *args do |event|
17
- params = [event.item, event.index, event.section].compact
17
+ params = [event.item, event.index, event.section]
18
18
  params = params.slice(0, block.arity) if block.arity > -1
19
19
 
20
20
  block.call *params
@@ -40,6 +40,7 @@ class UnderOs::UI::Collection < UnderOs::UI::View
40
40
 
41
41
  def reload
42
42
  @_.reloadData
43
+ self
43
44
  end
44
45
 
45
46
  def number_of_items(section=0)
@@ -48,6 +49,7 @@ class UnderOs::UI::Collection < UnderOs::UI::View
48
49
 
49
50
  def number_of_items=(value)
50
51
  @number_of_items = value.is_a?(Numeric) ? [value] : value
52
+ reload
51
53
  end
52
54
 
53
55
  def number_of_sections
@@ -1,7 +1,7 @@
1
1
  class UnderOs::UI::Locker < UnderOs::UI::View
2
2
  wraps UIView, tag: :locker
3
3
 
4
- attr_reader :label, :sinner
4
+ attr_reader :label, :spinner
5
5
 
6
6
  def initialize(options={})
7
7
  super options
@@ -22,39 +22,33 @@ module UnderOs::UI
22
22
 
23
23
  def textAlign
24
24
  if @view.is_a?(UIButton)
25
- case @view.contentHorizontalAlignment
26
- when UIControlContentHorizontalAlignmentRight then 'right'
27
- when UIControlContentHorizontalAlignmentCenter then 'center'
28
- when UIControlContentHorizontalAlignmentFill then 'justify'
29
- else 'left'
30
- end
25
+ BUTTONS_ALIGMENTS_MAP.key(@view.contentHorizontalAlignment)
31
26
  elsif @view.respond_to?(:textAlignment)
32
- case @view.textAlignment
33
- when NSTextAlignmentRight then 'right'
34
- when NSTextAlignmentCenter then 'center'
35
- when NSTextAlignmentJustified then 'justify'
36
- else 'left'
37
- end
27
+ TEXTNODES_ALIGMENTS_MAP.key(@view.textAlignment)
38
28
  end
39
29
  end
40
30
 
41
31
  def textAlign=(value)
42
32
  if @view.is_a?(UIButton)
43
- @view.contentHorizontalAlignment = case value.to_s
44
- when 'right' then UIControlContentHorizontalAlignmentRight
45
- when 'center' then UIControlContentHorizontalAlignmentCenter
46
- when 'justify' then UIControlContentHorizontalAlignmentFill
47
- else UIControlContentHorizontalAlignmentLeft
48
- end
33
+ @view.contentHorizontalAlignment = BUTTONS_ALIGMENTS_MAP[value.to_s] || BUTTONS_ALIGMENTS_MAP['left']
49
34
  elsif @view.respond_to?(:textAlignment)
50
- @view.textAlignment = case value.to_s
51
- when 'right' then NSTextAlignmentRight
52
- when 'center' then NSTextAlignmentCenter
53
- when 'justify' then NSTextAlignmentJustified
54
- else NSTextAlignmentLeft
55
- end
35
+ @view.textAlignment = TEXTNODES_ALIGMENTS_MAP[value.to_s] || BUTTONS_ALIGMENTS_MAP['left']
56
36
  end
57
37
  end
38
+
39
+ BUTTONS_ALIGMENTS_MAP = {
40
+ 'right' => UIControlContentHorizontalAlignmentRight,
41
+ 'center' => UIControlContentHorizontalAlignmentCenter,
42
+ 'justify' => UIControlContentHorizontalAlignmentFill,
43
+ 'left' => UIControlContentHorizontalAlignmentLeft
44
+ }
45
+
46
+ TEXTNODES_ALIGMENTS_MAP = {
47
+ 'right' => NSTextAlignmentRight,
48
+ 'center' => NSTextAlignmentCenter,
49
+ 'justify' => NSTextAlignmentJustified,
50
+ 'left' => NSTextAlignmentLeft
51
+ }
58
52
  end
59
53
  end
60
54
  end
@@ -19,11 +19,8 @@ module UnderOs::UI
19
19
  end
20
20
 
21
21
  def margin=(value)
22
- value = to_4dim_array(value)
23
- self.marginTop = value[0]
24
- self.marginLeft = value[3]
25
- self.marginRight = value[1]
26
- self.marginBottom = value[2]
22
+ @margin_top, @margin_right, @margin_botom, @margin_left = to_4dim_array(value)
23
+ set_offsets
27
24
  end
28
25
 
29
26
  def marginTop
@@ -67,11 +64,8 @@ module UnderOs::UI
67
64
  end
68
65
 
69
66
  def padding=(value)
70
- value = to_4dim_array(value)
71
- self.paddingTop = value[0]
72
- self.paddingLeft = value[3]
73
- self.paddingRight = value[1]
74
- self.paddingBottom = value[2]
67
+ @padding_top, @padding_right, @padding_botom, @padding_left = to_4dim_array(value)
68
+ set_offsets
75
69
  end
76
70
 
77
71
  def paddingTop
@@ -113,10 +107,7 @@ module UnderOs::UI
113
107
  private
114
108
 
115
109
  def to_4dim_array(value)
116
- if value.is_a?(String)
117
- value = value.gsub('px', '').strip.split().reject(&:blank?).map(&:to_f)
118
- end
119
-
110
+ value = value.gsub('px', '').split.map(&:to_f) if value.is_a?(String)
120
111
  value = Array(value)
121
112
  case value.size
122
113
  when 1 then value * 4
@@ -128,32 +119,45 @@ module UnderOs::UI
128
119
  def set_offsets
129
120
  return nil if display != :inline
130
121
 
131
- position_x = marginLeft
132
- position_y = marginTop
122
+ position_x = marginLeft + parent_offset_x
123
+ position_y = marginTop
133
124
 
134
- parent_view = view.superview
135
- previous_view = parent_view.subviews[parent_view.subviews.index(view)-1] unless parent_view.subviews[0] === view
125
+ @view.frame = [[position_x, position_y], [
126
+ @view.frame.size.width, @view.frame.size.height
127
+ ]]
128
+ end
129
+
130
+ def set_paddings
131
+ if @view.respond_to?(:contentEdgeInsets)
132
+ @view.contentEdgeInsets = UIEdgeInsetsMake(paddingTop, paddingLeft, paddingBottom, paddingRight)
133
+ end
134
+ end
136
135
 
137
- if previous_view
138
- pos = previous_view.frame.origin
139
- size = previous_view.frame.size
136
+ def parent_offset_x
137
+ offset = 0
138
+ parent_frame = parent_view_frame
140
139
 
141
- position_x += pos.x + size.width
140
+ if parent_frame
141
+ offset += parent_frame.origin.x + parent_frame.size.width
142
142
 
143
- if wrap = UnderOs::UI::View.wrap_for(view)
144
- position_x += wrap.style.marginRight
143
+ if wrap = UnderOs::UI::View.wrap_for(@view)
144
+ offset += wrap.style.marginRight
145
145
  end
146
146
  end
147
147
 
148
- view.frame = [[position_x, position_y],[
149
- view.frame.size.width, view.frame.size.height
150
- ]]
148
+ offset
151
149
  end
152
150
 
153
- def set_paddings
154
- if @view.respond_to?(:contentEdgeInsets)
155
- @view.contentEdgeInsets = UIEdgeInsetsMake(paddingTop, paddingLeft, paddingBottom, paddingRight)
151
+ def parent_view_frame
152
+ parent_view = @view.superview
153
+ parent_frame = nil
154
+
155
+ unless parent_view.subviews[0] === @view
156
+ previous_view = parent_view.subviews[parent_view.subviews.index(@view)-1]
157
+ parent_frame = previous_view && previous_view.frame
156
158
  end
159
+
160
+ parent_frame
157
161
  end
158
162
  end
159
163
  end
data/lib/under_os.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module UnderOs
2
- VERSION='1.0.0'
2
+ VERSION='1.1.0'
3
3
  end
4
4
 
5
5
  UOS = UnderOS = UnderOs
@@ -0,0 +1,19 @@
1
+ describe Hash do
2
+ describe '#to_query' do
3
+ it "converts a simple hash into a query string" do
4
+ {a: 1, b: 2}.to_query.should == 'a=1&b=2'
5
+ end
6
+
7
+ it "converts nested hashes into queries correctly" do
8
+ {a: 1, b: {c: {d: 4}}}.to_query.should == 'a=1&b%5Bc%5D%5Bd%5D=4'
9
+ end
10
+
11
+ it "handles array values correctly" do
12
+ {a: [1,2,3,4]}.to_query.should == 'a%5B%5D=1&a%5B%5D=2&a%5B%5D=3&a%5B%5D=4'
13
+ end
14
+
15
+ it "escapes any URL sensitive characters in the values" do
16
+ {a: '%= '}.to_query.should == 'a=%25=%20'
17
+ end
18
+ end
19
+ 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
@@ -84,4 +84,38 @@ describe String do
84
84
  }.should.raise(NameError)
85
85
  end
86
86
  end
87
+
88
+ describe '#url_encode' do
89
+ it "escapes all the URL sensitive symbols" do
90
+ "!*'\"();:@&=+$,/?%#[]% ".url_encode.should == "!*'%22();:@&=+$,/?%25%23%5B%5D%25%20"
91
+ end
92
+
93
+ it "doesn't touch normal characters" do
94
+ "Hello World".url_encode.should == "Hello%20World"
95
+ end
96
+ end
97
+
98
+ describe '#url_decode' do
99
+ it "converts an url encoded string back to normality" do
100
+ "!*'%22();:@&=+$,/?%25%23%5B%5D%25%20".url_decode.should == "!*'\"();:@&=+$,/?%#[]% "
101
+ end
102
+
103
+ it "doesn't break normal characters" do
104
+ "Hello%20World".url_decode.should == "Hello World"
105
+ end
106
+ end
107
+
108
+ describe '#to_data' do
109
+ before do
110
+ @data = "ohai there".to_data
111
+ end
112
+
113
+ it "converts the string into a data object" do
114
+ @data.is_a?(NSData).should == true
115
+ end
116
+
117
+ it "converts it into data with correct content" do
118
+ @data.to_s.should == "ohai there"
119
+ end
120
+ end
87
121
  end
@@ -0,0 +1,13 @@
1
+ describe UnderOS::HTTP::Cookies do
2
+ before do
3
+ @cookies = UnderOS::HTTP::Cookies.new({a: 1, b: true}, "http://test.com")
4
+ end
5
+
6
+ it "allows to export them in a string" do
7
+ @cookies.to_s.should == "a=1; b=true"
8
+ end
9
+
10
+ it "allows to export them into an HTTP header hash" do
11
+ @cookies.headers.should == {"Cookie"=>"a=1; b=true"}
12
+ end
13
+ end
@@ -0,0 +1,44 @@
1
+ describe UnderOS::HTTP::Request do
2
+ before do
3
+ @url = "http://test.com"
4
+ @request = UnderOS::HTTP::Request.new(@url, {
5
+ headers: {my: 'headers'},
6
+ cookies: {my: 'cookies'},
7
+ params: {my: 'params'},
8
+ method: :put
9
+ })
10
+ end
11
+
12
+ describe "#constructor" do
13
+ it "assigns the url correctly" do
14
+ @request.url.should == @url
15
+ end
16
+
17
+ it "assigns the request headers correctly" do
18
+ @request.headers.should == {my: 'headers'}
19
+ end
20
+
21
+ it "assigns the request cookies correctly" do
22
+ @request.cookies.should == {my: 'cookies'}
23
+ end
24
+
25
+ it "assigns my params correctly" do
26
+ @request.params.should == {my: 'params'}
27
+ end
28
+
29
+ it "assigns the request method correctly" do
30
+ @request.method.should == 'PUT'
31
+ end
32
+ end
33
+
34
+ describe "#on" do
35
+ it "returns the response object instead of an event into a callback" do
36
+ @response = nil
37
+
38
+ @request.on(:smth) { |r| @response = r }
39
+ @request.emit(:smth, response: 'response')
40
+
41
+ @response.should == 'response'
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,20 @@
1
+ describe UnderOs::HTTP do
2
+ extend Facon::SpecHelpers
3
+
4
+ before do
5
+ @url = 'http://test.com'
6
+ @req = mock 'request', send: :request
7
+ end
8
+
9
+ %w[get post put patch head delete].each do |method|
10
+ describe ".#{method}" do
11
+ it "sends a #{method.upcase} request" do
12
+ UnderOs::HTTP::Request.should.receive(:new)
13
+ .with(@url, foo: 'bar', method: method.to_sym)
14
+ .and_return(@req)
15
+
16
+ UnderOs::HTTP.__send__(method, @url, foo: 'bar').should == :request
17
+ end
18
+ end
19
+ end
20
+ end
data/under-os.gemspec CHANGED
@@ -16,8 +16,6 @@ Gem::Specification.new do |gem|
16
16
  gem.executables = gem.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
17
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
18
 
19
- gem.add_development_dependency 'rake'
20
-
21
19
  gem.post_install_message = %Q{
22
20
  If you want to use the UnderOS template with RubyMotion, please run the following:
23
21
 
@@ -29,4 +27,8 @@ Gem::Specification.new do |gem|
29
27
 
30
28
  motion create awesomness --template=uos
31
29
  }
30
+
31
+ gem.add_development_dependency 'rake'
32
+ gem.add_development_dependency 'motion-facon'
33
+
32
34
  end
metadata CHANGED
@@ -1,27 +1,41 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: under-os
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nikolay Nemshilov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-12-13 00:00:00.000000000 Z
11
+ date: 2013-12-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
17
+ - - '>='
18
18
  - !ruby/object:Gem::Version
19
19
  version: '0'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ">="
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: motion-facon
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
25
39
  - !ruby/object:Gem::Version
26
40
  version: '0'
27
41
  description: A web-broser like wrapper over iOS
@@ -31,7 +45,8 @@ executables: []
31
45
  extensions: []
32
46
  extra_rdoc_files: []
33
47
  files:
34
- - ".gitignore"
48
+ - .gitignore
49
+ - .travis.yml
35
50
  - Gemfile
36
51
  - Gemfile.lock
37
52
  - README.md
@@ -57,7 +72,6 @@ files:
57
72
  - app/pages/navbar_page.rb
58
73
  - app/pages/scrolls_page.rb
59
74
  - app/pages/sidebars_page.rb
60
- - app/pages/stuff_page.rb
61
75
  - app/styles/application.css
62
76
  - app/styles/calculator.css
63
77
  - app/styles/collections.css
@@ -73,14 +87,19 @@ files:
73
87
  - lib/under_os/app.rb
74
88
  - lib/under_os/color.rb
75
89
  - lib/under_os/config.rb
90
+ - lib/under_os/core/array.rb
91
+ - lib/under_os/core/hash.rb
92
+ - lib/under_os/core/json.rb
76
93
  - lib/under_os/core/kernel.rb
77
94
  - lib/under_os/core/numeric.rb
95
+ - lib/under_os/core/object.rb
78
96
  - lib/under_os/core/string.rb
79
97
  - lib/under_os/delegate.rb
80
98
  - lib/under_os/events.rb
81
99
  - lib/under_os/file.rb
82
100
  - lib/under_os/history.rb
83
101
  - lib/under_os/http.rb
102
+ - lib/under_os/http/cookies.rb
84
103
  - lib/under_os/http/receiver.rb
85
104
  - lib/under_os/http/request.rb
86
105
  - lib/under_os/http/response.rb
@@ -144,11 +163,16 @@ files:
144
163
  - spec/assets/test.css
145
164
  - spec/assets/test.html
146
165
  - spec/assets/test_page.rb
166
+ - spec/lib/core/hash_spec.rb
167
+ - spec/lib/core/json_spec.rb
147
168
  - spec/lib/core/numeric_spec.rb
148
169
  - spec/lib/core/string_spec.rb
149
170
  - spec/lib/under_os/color_spec.rb
150
171
  - spec/lib/under_os/events_spec.rb
151
172
  - spec/lib/under_os/file_spec.rb
173
+ - spec/lib/under_os/http/cookies_spec.rb
174
+ - spec/lib/under_os/http/request_spec.rb
175
+ - spec/lib/under_os/http_spec.rb
152
176
  - spec/lib/under_os/page/builder_spec.rb
153
177
  - spec/lib/under_os/page/layout_spec.rb
154
178
  - spec/lib/under_os/page/matcher_spec.rb
@@ -201,17 +225,17 @@ require_paths:
201
225
  - lib
202
226
  required_ruby_version: !ruby/object:Gem::Requirement
203
227
  requirements:
204
- - - ">="
228
+ - - '>='
205
229
  - !ruby/object:Gem::Version
206
230
  version: '0'
207
231
  required_rubygems_version: !ruby/object:Gem::Requirement
208
232
  requirements:
209
- - - ">="
233
+ - - '>='
210
234
  - !ruby/object:Gem::Version
211
235
  version: '0'
212
236
  requirements: []
213
237
  rubyforge_project:
214
- rubygems_version: 2.0.3
238
+ rubygems_version: 2.1.11
215
239
  signing_key:
216
240
  specification_version: 4
217
241
  summary: A web-broser like wrapper over iOS using RubyMotion
@@ -220,11 +244,16 @@ test_files:
220
244
  - spec/assets/test.css
221
245
  - spec/assets/test.html
222
246
  - spec/assets/test_page.rb
247
+ - spec/lib/core/hash_spec.rb
248
+ - spec/lib/core/json_spec.rb
223
249
  - spec/lib/core/numeric_spec.rb
224
250
  - spec/lib/core/string_spec.rb
225
251
  - spec/lib/under_os/color_spec.rb
226
252
  - spec/lib/under_os/events_spec.rb
227
253
  - spec/lib/under_os/file_spec.rb
254
+ - spec/lib/under_os/http/cookies_spec.rb
255
+ - spec/lib/under_os/http/request_spec.rb
256
+ - spec/lib/under_os/http_spec.rb
228
257
  - spec/lib/under_os/page/builder_spec.rb
229
258
  - spec/lib/under_os/page/layout_spec.rb
230
259
  - spec/lib/under_os/page/matcher_spec.rb
@@ -1,35 +0,0 @@
1
- class StuffPage < UnderOs::Page
2
- def initialize
3
- self.title = "Various Stuff"
4
-
5
- @box = View.new(style: {
6
- top: 50,
7
- left: 50,
8
- width: 100,
9
- height: 100,
10
- background: :red,
11
- borderRadius: 10,
12
- borderWidth: 2,
13
- borderColor: :green
14
- })
15
-
16
- @box.on(:tap){ @box.fade_out.fade_in }
17
-
18
- @b2 = View.new(style: {background: :blue})
19
- @b2.size = {x: 50, y: 50}
20
- @b2.position = {x: 25, y: 25}
21
-
22
- @b2.on(:tap, :highlight)
23
-
24
- insert @box.insert(@b2)
25
-
26
- insert Button.new(text: 'Boo Hoo!').position(x: 100, y: 200)
27
-
28
- insert Label.new(text: "Hey Label", style: {background: :red}).position({x: 100, y: 250})
29
-
30
- insert Icon.new(:cog).position(x: 50, y: 300)
31
- insert Icon.new(:trash).position(x: 100, y: 300)
32
- insert Icon.new(:home).position(x: 150, y: 300)
33
- insert Icon.new(:magic).position(x: 200, y: 300)
34
- end
35
- end