teacup 0.0.1.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/.gitignore +3 -0
  2. data/Dofile +6 -0
  3. data/Gemfile +3 -0
  4. data/Gemfile.lock +26 -0
  5. data/README.md +151 -0
  6. data/Rakefile +39 -0
  7. data/lib/teacup.rb +41 -0
  8. data/lib/teacup/contributors.rb +7 -0
  9. data/lib/teacup/core_extensions/ui_view.rb +4 -0
  10. data/lib/teacup/core_extensions/ui_view_controller.rb +62 -0
  11. data/lib/teacup/layout.rb +207 -0
  12. data/lib/teacup/style_sheet.rb +195 -0
  13. data/lib/teacup/version.rb +5 -0
  14. data/lib/teacup/view.rb +123 -0
  15. data/pkg/teacup-0.0.0.gem +0 -0
  16. data/pkg/teacup-0.0.1.gem +0 -0
  17. data/proposals/other/README.md +45 -0
  18. data/proposals/other/app/config/application.rb +1 -0
  19. data/proposals/other/app/config/boot.rb +1 -0
  20. data/proposals/other/app/config/initializers/twitter.rb +7 -0
  21. data/proposals/other/app/controllers/events_controller.rb +28 -0
  22. data/proposals/other/app/controllers/venues_controller.rb +4 -0
  23. data/proposals/other/app/db/README.md +16 -0
  24. data/proposals/other/app/db/migrations/20120514201043_create_events.rb +9 -0
  25. data/proposals/other/app/db/migrations/20120514201044_add_price_to_events.rb +5 -0
  26. data/proposals/other/app/db/migrations/20120514201045_create_venues.rb +8 -0
  27. data/proposals/other/app/db/schema.rb +19 -0
  28. data/proposals/other/app/models/event.rb +14 -0
  29. data/proposals/other/app/models/venue.rb +3 -0
  30. data/proposals/other/app/views/events/edit.ipad.rb +8 -0
  31. data/proposals/other/app/views/events/edit.iphone.rb +7 -0
  32. data/proposals/other/app/views/events/show.ipad.rb +2 -0
  33. data/proposals/other/app/views/events/show.iphone.rb +3 -0
  34. data/samples/Hai/.gitignore +5 -0
  35. data/samples/Hai/Rakefile +12 -0
  36. data/samples/Hai/app/app_delegate.rb +9 -0
  37. data/samples/Hai/app/hai_controller.rb +9 -0
  38. data/samples/Hai/spec/main_spec.rb +9 -0
  39. data/samples/Hai/styles/ipad.rb +11 -0
  40. data/samples/Hai/styles/ipad_vertical.rb +3 -0
  41. data/samples/README.md +4 -0
  42. data/spec/spec_helper.rb +5 -0
  43. data/spec/teacup/contributions_spec.rb +13 -0
  44. data/spec/teacup/version_spec.rb +9 -0
  45. data/teacup.gemspec +27 -0
  46. metadata +130 -0
@@ -0,0 +1,3 @@
1
+ .DS_Store
2
+ .rspec
3
+ .sass-cache/
data/Dofile ADDED
@@ -0,0 +1,6 @@
1
+ require 'do_it'
2
+
3
+ tasks = DoIt.new do
4
+ todo 'Make a "nicer looking" sample app (or improve existing app).'
5
+ todo 'Extend wiki on Github.'
6
+ end
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source :rubygems
2
+
3
+ gemspec
@@ -0,0 +1,26 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ teacup (0.0.1)
5
+ rake
6
+
7
+ GEM
8
+ remote: http://rubygems.org/
9
+ specs:
10
+ diff-lcs (1.1.3)
11
+ rake (0.9.2.2)
12
+ rspec (2.10.0)
13
+ rspec-core (~> 2.10.0)
14
+ rspec-expectations (~> 2.10.0)
15
+ rspec-mocks (~> 2.10.0)
16
+ rspec-core (2.10.1)
17
+ rspec-expectations (2.10.0)
18
+ diff-lcs (~> 1.1.3)
19
+ rspec-mocks (2.10.1)
20
+
21
+ PLATFORMS
22
+ ruby
23
+
24
+ DEPENDENCIES
25
+ rspec
26
+ teacup!
@@ -0,0 +1,151 @@
1
+ Teacup
2
+ ======
3
+
4
+ A community-driven DSL for creating user interfaces on the iphone.
5
+
6
+ By including the `Teacup` module, you can easily create layouts that adhere to
7
+ the [iOS user interface guidelines][iOS HIG], and it's an easy way to assign labels,
8
+ delegators, and datasources.
9
+
10
+ The goal is not to simply rename CocoaTouch method names, but to offer a
11
+ rubyesque (well, actually a rubymotion-esque) way to create an interface.
12
+
13
+ Using stylesheets and layouts, it makes coding an iOS app like designing a website with HTML and CSS.
14
+
15
+ **Check out a working sample app [here](https://github.com/rubymotion/teacup/tree/master/samples/Hai)!**
16
+
17
+ #### Installation
18
+
19
+ ```bash
20
+ $ git submodule add https://github.com:rubymotion/teacup vendor/teacup
21
+ ```
22
+
23
+ Then add the teacup library to your Rakefile:
24
+ ```
25
+ # Add libraries *before* your app so that you can access constants they define safely
26
+ #
27
+ dirs = ['vendor/teacup/lib', 'app']
28
+
29
+ # require all the directories in order.
30
+ app.files = dirs.map{|d| Dir.glob(File.join(app.project_dir, "#{d}/**/*.rb")) }.flatten
31
+ ```
32
+
33
+
34
+ #### Showdown
35
+
36
+ Regular
37
+
38
+ ```ruby
39
+ class SomeController < UIViewController
40
+ def viewDidLoad
41
+
42
+ @field = UITextField.new
43
+ @field.height = 50
44
+ @field.width = 200
45
+ view.addSubview(@field)
46
+
47
+ @search = UITextField.new
48
+ @search.height = 50
49
+ @search.width = 200
50
+ @search.placeholder = 'Find something...'
51
+ view.addSubview(@search)
52
+
53
+ true
54
+ end
55
+ end
56
+ ```
57
+
58
+ Teacup
59
+
60
+ ```ruby
61
+ # Stylesheet
62
+
63
+ Teacup::StyleSheet.new(:IPhone) do
64
+
65
+ style :field,
66
+ height: 50,
67
+ width: 200
68
+
69
+ style :search, extends: :field,
70
+ placeholder: 'Foo...'
71
+
72
+ end
73
+
74
+ # Controller
75
+
76
+ class SomeController < UIViewController
77
+
78
+ def viewDidLoad
79
+ view.addSubview(Teacup.style(:field, UITextField.new))
80
+ view.addSubview(Teacup.style(:search))
81
+ true
82
+ end
83
+
84
+ end
85
+ ```
86
+
87
+ Development
88
+ -----------
89
+
90
+ *Current version*: v0.0.0 (or see `lib/teacup/version.rb`)
91
+
92
+ *Last milestone*: Pick a name
93
+
94
+ *Next milestone*: Pick a DSL
95
+
96
+ teacup, being a community project, moves in "spurts" of decision making and
97
+ coding. Only the name — both the least and most important part :-) — is
98
+ decided.
99
+
100
+ We would love suggestions of any sort, and we're always free over at the `#teacuprb` channel on `irc.freenode.org`.
101
+
102
+
103
+ Ideas that proposals should keep in mind
104
+ ----------------------------------------
105
+
106
+ 1. output will conform, unless explicitly *disabled* to the [iOS HIG][]
107
+ 2. should provide a few useful layouts (see [readme for layout proposals](teacup/tree/master/proposals/layout)):
108
+ * basic: vertically arranged "things", or
109
+ * form: label/input combinations arranged in a table
110
+ * navbar: with ability to customize the buttons that get placed at the top
111
+ * tabbar: similar, but with the tabs at the bottom instead of nav at the top
112
+ * splitview: A splitview controller (for iPad Apps) with sane navigation defaults, nice loading webviews and JSON to populate the items in the popover menu
113
+
114
+ 3. layouts should have ways of placing things relative to edges, so placing a
115
+ label or nav at the "bottom" that spans the entire width should be *easy*.
116
+ (This means we'll need to check for ipad or iphone.)
117
+ 4. actions are either blocks, defined inline, or target/action combos (e.g.
118
+ `target: self, action: :my_method`)
119
+ 5. there should be a consistent "styling" language, preferably in a separate
120
+ file, something that could be handed to a designer. this is the BIG item!
121
+ 6. teacup should take a little `config` block for easy configuration
122
+ 7. Ideally, there should be some way to "inherit" styles in this language. So you can define a basic layout for all platforms and then tweak (see [readme for style proposals](teacup/tree/master/proposals/styles))
123
+
124
+ [iOS HIG]: http://developer.apple.com/library/ios/#DOCUMENTATION/UserExperience/Conceptual/MobileHIG/Introduction/Introduction.html
125
+
126
+ Bugs
127
+ ----
128
+
129
+ Please, do *not* hesitate to report any bugs you find with our source at the [Issues](https://github.com/rubymotion/teacup/issues) page.
130
+
131
+ Actual proposals
132
+ ----------------
133
+
134
+ 1. [stylesheet][Commune], by [ConradIrwin][]
135
+ 2. [teacup][teacup_colinta], by [colinta][]
136
+ 3. [style][style_by_beakr], by [Beakr][]
137
+ 3. [layout][layout_by_beakr], by [Beakr][]
138
+ 4. [layout][layout_by_farcaller], by [farcaller][]
139
+ 5. [hybrid][], by [colinta][]
140
+
141
+ [Commune]: https://github.com/colinta/teacup/blob/master/proposals/styles/stylesheet_by_conradirwin.rb
142
+ [teacup_colinta]: https://github.com/colinta/teacup/blob/master/proposals/styles/teacup_by_colinta.rb
143
+ [style_by_beakr]: https://github.com/colinta/teacup/blob/master/proposals/layout/beakr_improved.rb
144
+ [layout_by_beakr]: https://github.com/colinta/teacup/blob/master/proposals/styles/beakr_improved.rb
145
+ [layout_by_farcaller]: https://github.com/colinta/teacup/blob/master/proposals/styles/layout_by_farcaller.rb
146
+ [hybrid]: https://github.com/colinta/teacup/blob/master/proposals/layout/hybrid_style_and_layout_by_colinta.rb
147
+
148
+ [ConradIrwin]: https://github.com/ConradIrwin
149
+ [colinta]: https://github.com/colinta
150
+ [farcaller]: https://github.com/farcaller
151
+ [Beakr]: https://github.com/Beakr
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env rake
2
+
3
+ # Bundler gem tasks ftw
4
+ require 'bundler/gem_tasks'
5
+
6
+ require 'rspec/core/rake_task'
7
+ require File.expand_path '../lib/teacup/version.rb', __FILE__
8
+
9
+ # Default task
10
+ task :default => :spec # Run spec
11
+ task :v => :version # Alt
12
+
13
+ # - - - - - - - - - - - - - - - - - - -
14
+ # Tasks
15
+ # - - - - - - - - - - - - - - - - - - -
16
+
17
+ desc 'display Teacup\'s current version'
18
+ task(:version) { version }
19
+
20
+ desc 'diesplay list of contributors'
21
+ task(:contrib) { contrib }
22
+
23
+ desc 'run RSpec tests'
24
+ RSpec::Core::RakeTask.new
25
+
26
+ desc 'build and install the gem'
27
+ task(:prep) { system('rake build; rake install') }
28
+
29
+ # - - - - - - - - - - - - - - - - - - -
30
+ # Helpers
31
+ # - - - - - - - - - - - - - - - - - - -
32
+
33
+ def version
34
+ puts "Teacup #{Teacup::VERSION}"
35
+ end
36
+
37
+ def contrib
38
+ puts Teacup::CONTRIBUTORS
39
+ end
@@ -0,0 +1,41 @@
1
+ unless defined?(Motion::Project::Config)
2
+ raise "This file must be required within a RubyMotion project Rakefile."
3
+ end
4
+
5
+ # In order that we can prepend our files to the load path after the user has
6
+ # configured their app.files, we need to run code *after* the user has set up
7
+ # their app.
8
+ #
9
+ # Unfortunately, the canonical place to require rubygems is at the top of the
10
+ # file (while we could just add to the instructions "please `require 'teacup'`
11
+ # at the bottom, that would be odd, and no-one would read the instructions).
12
+ #
13
+ # To this end, we tweak the App setup function so that whenever setup is called,
14
+ # we configure teacup after that.
15
+ #
16
+ # This is not great though, as other gems may (following the instructions at
17
+ # http://reality.hk/posts/2012/05/22/create-gems-for-rubymotion/) also call
18
+ # setup...
19
+ #
20
+ # For sanity reasons, we therefore delete teacup requires from the load order
21
+ # and re-add them to the front every single time {setup} is called.
22
+ #
23
+ # TODO: We should send a patch to rubymotion that adds first-class support for
24
+ # app.gems. These could then be required *after* the user has finished running
25
+ # the setup block, so that they can just run setup properly.
26
+ #
27
+ class << Motion::Project::App
28
+
29
+ def setup_with_teacup
30
+ setup_without_teacup do |app|
31
+ yield app
32
+
33
+ dirs = %w(teacup teacup/core_extensions)
34
+ files = dirs.map{ |dir| Dir.glob("#{File.dirname(__FILE__)}/#{dir}/*.rb") }.flatten
35
+ app.files = files + (app.files - files)
36
+ end
37
+ end
38
+
39
+ alias setup_without_teacup setup
40
+ alias setup setup_with_teacup
41
+ end
@@ -0,0 +1,7 @@
1
+ module Teacup
2
+
3
+ # If you've made a contribution, add your name here in :' ... ' as a constant symbol.
4
+ # Please place names in aphabetical order.
5
+ CONTRIBUTORS = ['Chris Clarke', 'Colin Thomas-Arnold', 'Conrad Irwin', 'Roland Oth', 'Vladimir Pouzanov']
6
+
7
+ end
@@ -0,0 +1,4 @@
1
+ class UIView
2
+ include Teacup::Layout
3
+ include Teacup::View
4
+ end
@@ -0,0 +1,62 @@
1
+ class UIViewController
2
+ include Teacup::Layout
3
+
4
+ class << self
5
+ # Define the layout of a controller's view.
6
+ #
7
+ # This function is analogous to Teacup::Layout#layout, though it is
8
+ # designed so you can create an entire layout in a declarative manner in
9
+ # your controller.
10
+ #
11
+ # The hope is that his declarativeness will allow us to automatically
12
+ # deal with common iOS programming tasks (like releasing views when
13
+ # low-memory conditions occur) for you. This is still not implemented
14
+ # though.
15
+ #
16
+ # @param name The stylename for your controller's view.
17
+ #
18
+ # @param properties Any extra styles that you want to apply.
19
+ #
20
+ # @param &block The block in which you should define your layout.
21
+ # It will be instance_exec'd in the context of a
22
+ # controller instance.
23
+ #
24
+ # @example
25
+ # MyViewController < UIViewController
26
+ # layout :my_view do
27
+ # subview UILabel, xjad: "Test"
28
+ # subview UITextField, {
29
+ # frame: [[200, 200], [100, 100]]
30
+ # delegate: self
31
+ # }
32
+ # subview UIView, :shiny_thing) {
33
+ # subview UIView, :centre_of_shiny_thing
34
+ # }
35
+ # end
36
+ # end
37
+ #
38
+ def layout(stylename, properties={}, &block)
39
+ @layout_definition = [stylename, properties, block]
40
+ end
41
+
42
+ # Retreive the layout defined by {layout}
43
+ def layout_definition
44
+ @layout_definition
45
+ end
46
+ end
47
+
48
+ # Instantiate the layout from the class, and then call layoutDidLoad.
49
+ #
50
+ # If you want to use Teacup in your controller, please hook into layoutDidLoad,
51
+ # not viewDidLoad.
52
+ def viewDidLoad
53
+ if self.class.layout_definition
54
+ name, properties, block = self.class.layout_definition
55
+ layout(view, name, properties, &block)
56
+ end
57
+
58
+ layoutDidLoad
59
+ end
60
+
61
+ def layoutDidLoad; true; end
62
+ end
@@ -0,0 +1,207 @@
1
+ module Teacup
2
+ # Teacup::Layout defines a layout function that can be used to configure the
3
+ # layout of views in your application.
4
+ #
5
+ # It is included into UIView and UIViewController directly so these functions
6
+ # should be available when you need them.
7
+ #
8
+ # In order to use layout() in a UIViewController most effectively you will want
9
+ # to define a stylesheet method that returns a stylesheet.
10
+ #
11
+ # @example
12
+ # class MyViewController < UIViewController
13
+ # interface(:my_view) do
14
+ # layout UIImage, :logo
15
+ # end
16
+ #
17
+ # def stylesheet
18
+ # Teacup::Stylesheet::Logo
19
+ # end
20
+ # end
21
+ #
22
+ module Layout
23
+
24
+ # Alter the layout of a view
25
+ #
26
+ # @param instance The first parameter is the view that you want to
27
+ # layout.
28
+ #
29
+ # @param name The second parameter is optional, and is the
30
+ # stylename to apply to the element. When using
31
+ # stylesheets any properties defined in the
32
+ # current stylesheet (see {stylesheet}) for this
33
+ # element will be immediately applied.
34
+ #
35
+ # @param properties The third parameter is optional, and is a Hash
36
+ # of properties to apply to the view directly.
37
+ #
38
+ # @param &block If a block is passed, it is evaluated such that
39
+ # any calls to {subview} that occur within that
40
+ # block cause created subviews to be added to *this*
41
+ # view instead of to the top-level view.
42
+ #
43
+ # For example, to alter the width and height of a carousel:
44
+ #
45
+ # @example
46
+ # layout(carousel, width: 500, height: 100)
47
+ #
48
+ # Or to layout the carousel in the default style:
49
+ #
50
+ # @example
51
+ # layout(carousel, :default_carousel)
52
+ #
53
+ # You can also use this method with {subview}, for example to add a new
54
+ # image to a carousel:
55
+ #
56
+ # @example
57
+ # layout(carousel) {
58
+ # subview(UIImage, backgroundColor: UIColor.colorWithImagePattern(image)
59
+ # }
60
+ #
61
+ def layout(instance, name_or_properties, properties_or_nil=nil, &block)
62
+ if properties_or_nil
63
+ name = name_or_properties.to_sym
64
+ properties = properties_or_nil
65
+ elsif Hash === name_or_properties
66
+ name = nil
67
+ properties = name_or_properties
68
+ else
69
+ name = name_or_properties.to_sym
70
+ properties = nil
71
+ end
72
+
73
+ instance.stylesheet = stylesheet
74
+ instance.style(properties) if properties
75
+ instance.stylename = name if name
76
+
77
+ begin
78
+ superview_chain << instance
79
+ instance_exec(instance, &block) if block_given?
80
+ ensure
81
+ superview_chain.pop
82
+ end
83
+
84
+ instance
85
+ end
86
+
87
+ # Add a new subview to the view heirarchy.
88
+ #
89
+ # By default the subview will be added at the top level of the view heirarchy, though
90
+ # if this function is executed within a block passed to {layout} or {subview}, then this
91
+ # view will be added as a subview of the instance being layed out by the block.
92
+ #
93
+ # This is particularly useful when coupled with the {UIViewController.heirarchy} function
94
+ # that allows you to declare your view heirarchy.
95
+ #
96
+ # @param class_or_instance The UIView subclass (or instance thereof) that you want
97
+ # to add. If you pass a class, an instance will be created
98
+ # by calling {new}.
99
+ #
100
+ # @param *args Arguments to pass to {layout} to instruct teacup how to
101
+ # lay out the newly added subview.
102
+ #
103
+ # @param &block A block to execute with the current view context set to
104
+ # your new element, see {layout} for more details.
105
+ #
106
+ # @return instance The instance that was added to the view heirarchy.
107
+ #
108
+ # For example, to specify that a controller should contain some labels:
109
+ #
110
+ # @example
111
+ # MyViewController < UIViewController
112
+ # heirarchy(:my_view) do
113
+ # subview(UILabel, text: 'Test')
114
+ # subview(UILabel, :styled_label)
115
+ # end
116
+ # end
117
+ #
118
+ # If you need to add a new image at runtime, you can also do that:
119
+ #
120
+ # @example
121
+ # layout(carousel) {
122
+ # subview(UIImage, backgroundColor: UIColor.colorWithImagePattern(image)
123
+ # }
124
+ #
125
+ def subview(class_or_instance, *args, &block)
126
+ instance = Class === class_or_instance ? class_or_instance.new : class_or_instance
127
+
128
+ if Class === class_or_instance
129
+ unless class_or_instance <= UIView
130
+ raise "Expected subclass of UIView, got: #{class_or_instance.inspect}"
131
+ end
132
+ instance = class_or_instance.new
133
+ elsif UIView === class_or_instance
134
+ instance = class_or_instance
135
+ else
136
+ raise "Expected a UIView, got: #{class_or_instance.inspect}"
137
+ end
138
+
139
+ (superview_chain.last || top_level_view).addSubview(instance)
140
+
141
+ layout(instance, *args, &block)
142
+
143
+ instance
144
+ end
145
+
146
+ # Returns a stylesheet to use to style the contents of this controller's
147
+ # view.
148
+ #
149
+ # This method will be queried each time {restyle!} is called, and also
150
+ # implicitly # whenever Teacup needs to draw your layout (currently only at
151
+ # view load time).
152
+ #
153
+ # @return Teacup::Stylesheet
154
+ #
155
+ # @example
156
+ #
157
+ # def stylesheet
158
+ # if [UIDeviceOrientationLandscapeLeft,
159
+ # UIDeviceOrientationLandscapeRight].include?(UIDevice.currentDevice.orientation)
160
+ # Teacup::Stylesheet::IPad
161
+ # else
162
+ # Teacup::Stylesheet::IPadVertical
163
+ # end
164
+ # end
165
+ def stylesheet
166
+ nil
167
+ end
168
+
169
+ # Instruct teacup to reapply styles to your subviews
170
+ #
171
+ # You should call this whenever the return value of your stylesheet meethod
172
+ # would change,
173
+ #
174
+ # @example
175
+ # def willRotateToInterfaceOrientation(io, duration: duration)
176
+ # restyle!
177
+ # end
178
+ def restyle!
179
+ top_level_view.stylesheet = stylesheet
180
+ end
181
+
182
+ protected
183
+
184
+ # Get's the top-level UIView for this object.
185
+ #
186
+ # This can either be 'self' if the current object is in fact a UIView,
187
+ # or 'view' if it's a controller.
188
+ #
189
+ # @return UIView
190
+ def top_level_view
191
+ case self
192
+ when UIViewController
193
+ view
194
+ when UIView
195
+ self
196
+ end
197
+ end
198
+
199
+ # Get's the current stack of views in nested calls to layout.
200
+ #
201
+ # The view at the end of the stack is the one into which subviews
202
+ # are currently being attached.
203
+ def superview_chain
204
+ @superview_chain ||= []
205
+ end
206
+ end
207
+ end