under-os 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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