teacup 0.0.1.pre
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +3 -0
- data/Dofile +6 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +26 -0
- data/README.md +151 -0
- data/Rakefile +39 -0
- data/lib/teacup.rb +41 -0
- data/lib/teacup/contributors.rb +7 -0
- data/lib/teacup/core_extensions/ui_view.rb +4 -0
- data/lib/teacup/core_extensions/ui_view_controller.rb +62 -0
- data/lib/teacup/layout.rb +207 -0
- data/lib/teacup/style_sheet.rb +195 -0
- data/lib/teacup/version.rb +5 -0
- data/lib/teacup/view.rb +123 -0
- data/pkg/teacup-0.0.0.gem +0 -0
- data/pkg/teacup-0.0.1.gem +0 -0
- data/proposals/other/README.md +45 -0
- data/proposals/other/app/config/application.rb +1 -0
- data/proposals/other/app/config/boot.rb +1 -0
- data/proposals/other/app/config/initializers/twitter.rb +7 -0
- data/proposals/other/app/controllers/events_controller.rb +28 -0
- data/proposals/other/app/controllers/venues_controller.rb +4 -0
- data/proposals/other/app/db/README.md +16 -0
- data/proposals/other/app/db/migrations/20120514201043_create_events.rb +9 -0
- data/proposals/other/app/db/migrations/20120514201044_add_price_to_events.rb +5 -0
- data/proposals/other/app/db/migrations/20120514201045_create_venues.rb +8 -0
- data/proposals/other/app/db/schema.rb +19 -0
- data/proposals/other/app/models/event.rb +14 -0
- data/proposals/other/app/models/venue.rb +3 -0
- data/proposals/other/app/views/events/edit.ipad.rb +8 -0
- data/proposals/other/app/views/events/edit.iphone.rb +7 -0
- data/proposals/other/app/views/events/show.ipad.rb +2 -0
- data/proposals/other/app/views/events/show.iphone.rb +3 -0
- data/samples/Hai/.gitignore +5 -0
- data/samples/Hai/Rakefile +12 -0
- data/samples/Hai/app/app_delegate.rb +9 -0
- data/samples/Hai/app/hai_controller.rb +9 -0
- data/samples/Hai/spec/main_spec.rb +9 -0
- data/samples/Hai/styles/ipad.rb +11 -0
- data/samples/Hai/styles/ipad_vertical.rb +3 -0
- data/samples/README.md +4 -0
- data/spec/spec_helper.rb +5 -0
- data/spec/teacup/contributions_spec.rb +13 -0
- data/spec/teacup/version_spec.rb +9 -0
- data/teacup.gemspec +27 -0
- metadata +130 -0
@@ -0,0 +1,195 @@
|
|
1
|
+
module Teacup
|
2
|
+
# Stylesheets in Teacup act as a central configuration mechanism,
|
3
|
+
# they have two aims:
|
4
|
+
#
|
5
|
+
# 1. Allow you to store "Details" away from the main body of your code.
|
6
|
+
# (controllers shouldn't have to be filled with style rules)
|
7
|
+
# 2. Allow you to easily re-use configuration in many places.
|
8
|
+
#
|
9
|
+
# The API really provides only two methods, {Stylesheet#style} to store properties
|
10
|
+
# on the Stylesheet; and {Stylesheet#query} to get them out again:
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# stylesheet = Teacup::Stylesheet.new
|
14
|
+
# stylesheet.style :buttons, :corners => :rounded
|
15
|
+
# # => nil
|
16
|
+
# stylesheet.query :buttons
|
17
|
+
# # => {:corners => :rounded}
|
18
|
+
#
|
19
|
+
# In addition to this, two separate mechanisms are provided for sharing
|
20
|
+
# configuration within stylesheets.
|
21
|
+
#
|
22
|
+
# Firstly, if you set the ':extends' property for a given stylename, then on lookup
|
23
|
+
# the Stylesheet will merge the properties for the ':extends' stylename into the return
|
24
|
+
# value. Conflicts are resolved so that properties with the original stylename
|
25
|
+
# are resolved in its favour.
|
26
|
+
#
|
27
|
+
# @example
|
28
|
+
# Teacup::Stylesheet.new(:IPad) do
|
29
|
+
# style :button,
|
30
|
+
# background: UIColor.blackColor,
|
31
|
+
# top: 100
|
32
|
+
#
|
33
|
+
# style :ok_button, extends: :button,
|
34
|
+
# title: "OK!",
|
35
|
+
# top: 200
|
36
|
+
#
|
37
|
+
# end
|
38
|
+
# Teacup::Stylesheet::IPad.query(:ok_button)
|
39
|
+
# # => {background: UIColor.blackColor, top: 200, title: "OK!"}
|
40
|
+
#
|
41
|
+
# Secondly, you can import Stylesheets into each other, in exactly the same way as you
|
42
|
+
# can include Modules into each other in Ruby. This allows you to share rules between
|
43
|
+
# Stylesheets.
|
44
|
+
#
|
45
|
+
# As you'd expect, conflicts are resolve so that the Stylesheet on which you call query
|
46
|
+
# has the highest precedence.
|
47
|
+
#
|
48
|
+
# @example
|
49
|
+
# Teacup::Stylesheet.new(:IPad) do
|
50
|
+
# style :ok_button,
|
51
|
+
# title: "OK!"
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
# Teacup::Stylesheet.new(:IPadVertical) do
|
55
|
+
# import :IPad
|
56
|
+
# style :ok_button,
|
57
|
+
# width: 80
|
58
|
+
# end
|
59
|
+
# Teacup::Stylesheet::IPadVertical.query(:ok_button)
|
60
|
+
# # => {title: "OK!", width: 80}
|
61
|
+
#
|
62
|
+
# The two merging mechanisms are considered independently, so you can override
|
63
|
+
# a property both in a ':extends' rule, and also in an imported Stylesheet. In such a
|
64
|
+
# a case the Stylesheet inclusion conflicts are resolved independently; and then in
|
65
|
+
# a second phase, the ':extends' chain is flattened.
|
66
|
+
#
|
67
|
+
class Stylesheet
|
68
|
+
attr_reader :name
|
69
|
+
|
70
|
+
# Create a new Stylesheet with the given name.
|
71
|
+
#
|
72
|
+
# @param name, The name to give.
|
73
|
+
# @param &block, The body of the Stylesheet instance_eval'd.
|
74
|
+
# @example
|
75
|
+
# Teacup::Stylesheet.new(:IPadVertical) do
|
76
|
+
# import :IPadBase
|
77
|
+
# style :continue_button,
|
78
|
+
# top: 50
|
79
|
+
# end
|
80
|
+
#
|
81
|
+
def initialize(name, &block)
|
82
|
+
@name = name.to_sym
|
83
|
+
Teacup::Stylesheet.const_set(@name, self)
|
84
|
+
instance_eval &block
|
85
|
+
self
|
86
|
+
end
|
87
|
+
|
88
|
+
# Include another Stylesheet into this one, the rules defined
|
89
|
+
# within it will have lower precedence than those defined here
|
90
|
+
# in the case that they share the same keys.
|
91
|
+
#
|
92
|
+
# @param Symbol the name of the stylesheet.
|
93
|
+
# @example
|
94
|
+
# Teacup::Stylesheet.new(:IPadVertical) do
|
95
|
+
# import :IPadBase
|
96
|
+
# import :VerticalTweaks
|
97
|
+
# end
|
98
|
+
def import(name_or_stylesheet)
|
99
|
+
if Stylesheet === name_or_stylesheet
|
100
|
+
imported << name_or_stylesheet.name
|
101
|
+
else
|
102
|
+
imported << name_or_stylesheet.to_sym
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Add a set of properties for a given stylename or set of stylenames.
|
107
|
+
#
|
108
|
+
# @param Symbol, *stylename
|
109
|
+
# @param Hash[Symbol, Object], properties
|
110
|
+
# @example
|
111
|
+
# Teacup::Stylesheet.new(:IPadBase) do
|
112
|
+
# style :pretty_button,
|
113
|
+
# backgroundColor: UIColor.blackColor
|
114
|
+
#
|
115
|
+
# style :continue_button, extends: :pretty_button,
|
116
|
+
# title: "Continue!",
|
117
|
+
# top: 50
|
118
|
+
# end
|
119
|
+
def style(*queries)
|
120
|
+
properties = queries.pop
|
121
|
+
queries.each do |stylename|
|
122
|
+
styles[stylename].update(properties)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# Get the properties defined for the given stylename, in this Stylesheet and all
|
127
|
+
# those that have been imported.
|
128
|
+
#
|
129
|
+
# If the ':extends' property is set, we then repeat the process with the value
|
130
|
+
# of that, and include them into the result with lower precedence.
|
131
|
+
#
|
132
|
+
# @param Symbol stylename, the stylename to look up.
|
133
|
+
# @return Hash[Symbol, *] the resulting properties.
|
134
|
+
# @example
|
135
|
+
# Teacup::Stylesheet::IPadBase.query(:continue_button)
|
136
|
+
# # => {backgroundColor: UIColor.blackColor, title: "Continue!", top: 50}
|
137
|
+
def query(stylename)
|
138
|
+
this_rule = properties_for(stylename)
|
139
|
+
|
140
|
+
if also_include = this_rule.delete(:extends)
|
141
|
+
query(also_include).merge(this_rule)
|
142
|
+
else
|
143
|
+
this_rule
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# A unique and hopefully meaningful description of this Object.
|
148
|
+
#
|
149
|
+
# @return String
|
150
|
+
def inspect
|
151
|
+
"Teacup::Stylesheet:#{name.inspect}"
|
152
|
+
end
|
153
|
+
|
154
|
+
protected
|
155
|
+
|
156
|
+
# Get the properties for a given stylename, including any properties
|
157
|
+
# defined on stylesheets that have been imported into this one, but not
|
158
|
+
# resolving ':extends' inheritance.
|
159
|
+
#
|
160
|
+
# @param Symbol stylename, the stylename to search for.
|
161
|
+
# @param Hash so_far, the properties already found in stylesheets with
|
162
|
+
# lower precedence than this one.
|
163
|
+
# @param Hash seen, the Stylesheets that we've already visited, this is
|
164
|
+
# to avoid pathological cases where stylesheets
|
165
|
+
# have been imported recursively.
|
166
|
+
# @return Hash
|
167
|
+
def properties_for(stylename, so_far={}, seen={})
|
168
|
+
return so_far if seen[self]
|
169
|
+
seen[self] = true
|
170
|
+
|
171
|
+
imported.each do |name|
|
172
|
+
unless Teacup::Stylesheet.const_defined?(name)
|
173
|
+
raise "Teacup tried to import Stylesheet:#{name} into Stylesheet:#{self.name}, but it didn't exist"
|
174
|
+
end
|
175
|
+
Teacup::Stylesheet.const_get(name).properties_for(stylename, so_far, seen)
|
176
|
+
end
|
177
|
+
|
178
|
+
so_far.update(styles[stylename])
|
179
|
+
end
|
180
|
+
|
181
|
+
# The list of Stylesheet names that have been imported into this one.
|
182
|
+
#
|
183
|
+
# @return Array[Symbol]
|
184
|
+
def imported
|
185
|
+
@imported ||= []
|
186
|
+
end
|
187
|
+
|
188
|
+
# The actual contents of this stylesheet as a Hash from stylename to properties.
|
189
|
+
#
|
190
|
+
# @return Hash[Symbol, Hash]
|
191
|
+
def styles
|
192
|
+
@styles ||= Hash.new{ |h, k| h[k] = {} }
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
data/lib/teacup/view.rb
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
module Teacup
|
2
|
+
# Teacup::View defines some utility functions for UIView that enable
|
3
|
+
# a lot of the magic for Teacup::Layout.
|
4
|
+
#
|
5
|
+
# Most users of teacup should be able to ignore the contents of this file
|
6
|
+
# for the most part.
|
7
|
+
module View
|
8
|
+
# The current stylename that is used to look up properties in the stylesheet.
|
9
|
+
attr_reader :stylename
|
10
|
+
|
11
|
+
# The current stylesheet will be looked at when properties are needed.
|
12
|
+
attr_reader :stylesheet
|
13
|
+
|
14
|
+
# Alter the stylename of this view.
|
15
|
+
#
|
16
|
+
# This will cause new styles to be applied from the stylesheet.
|
17
|
+
#
|
18
|
+
# @param Symbol stylename
|
19
|
+
def stylename=(stylename)
|
20
|
+
@stylename = stylename
|
21
|
+
style(stylesheet.query(stylename)) if stylesheet
|
22
|
+
end
|
23
|
+
|
24
|
+
# Alter the stylesheet of this view.
|
25
|
+
#
|
26
|
+
# This will cause new styles to be applied using the current stylename,
|
27
|
+
# and will recurse into subviews.
|
28
|
+
#
|
29
|
+
# If you would prefer that a given UIView object does not inherit the
|
30
|
+
# stylesheet from its parents, override the 'stylesheet' method to
|
31
|
+
# return the correct value at all times.
|
32
|
+
#
|
33
|
+
# @param Teacup::Stylesheet stylesheet.
|
34
|
+
def stylesheet=(stylesheet)
|
35
|
+
@stylesheet = stylesheet
|
36
|
+
style(stylesheet.query(stylename)) if stylename && stylesheet
|
37
|
+
subviews.each{ |subview| subview.stylesheet = stylesheet }
|
38
|
+
end
|
39
|
+
|
40
|
+
# Animate a change to a new stylename.
|
41
|
+
#
|
42
|
+
# This is equivalent to wrapping a call to .stylename= inside
|
43
|
+
# UIView.beginAnimations.
|
44
|
+
#
|
45
|
+
# @param Symbol the new stylename
|
46
|
+
# @param Options the options for the animation (may include the
|
47
|
+
# duration and the curve)
|
48
|
+
#
|
49
|
+
def animate_to_stylename(stylename, options={})
|
50
|
+
return if self.stylename == stylename
|
51
|
+
UIView.beginAnimations(nil, context: nil)
|
52
|
+
# TODO: This should be in a style-sheet!
|
53
|
+
UIView.setAnimationDuration(options[:duration]) if options[:duration]
|
54
|
+
UIView.setAnimationCurve(options[:curve]) if options[:curve]
|
55
|
+
self.stylename = stylename
|
56
|
+
UIView.commitAnimations
|
57
|
+
end
|
58
|
+
|
59
|
+
# Apply style properties to this element.
|
60
|
+
#
|
61
|
+
# Takes a hash of properties such as may have been read from a stylesheet
|
62
|
+
# or passed as parameters to {Teacup::Layout#layout}, and applies them to
|
63
|
+
# the element.
|
64
|
+
#
|
65
|
+
# Does a little bit of magic (that may be split out as 'sugarcube') to
|
66
|
+
# make properties work as you'd expect.
|
67
|
+
#
|
68
|
+
# If you try and assign something in properties that is not supported,
|
69
|
+
# a warning message will be emitted.
|
70
|
+
#
|
71
|
+
# @param Hash the properties to set.
|
72
|
+
def style(properties)
|
73
|
+
clean_properties! properties
|
74
|
+
|
75
|
+
properties.each do |key, value|
|
76
|
+
if key == :title && UIButton === self
|
77
|
+
setTitle(value, forState: UIControlStateNormal)
|
78
|
+
elsif respond_to?(:"#{key}=")
|
79
|
+
send(:"#{key}=", value)
|
80
|
+
elsif layer.respond_to?(:"#{key}=")
|
81
|
+
layer.send(:"#{key}=", value)
|
82
|
+
elsif key == :keyboardType
|
83
|
+
setKeyboardType(value)
|
84
|
+
else
|
85
|
+
$stderr.puts "Teacup WARN: Can't apply #{key} to #{inspect}"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
#OUCH! Figure out why this is needed
|
90
|
+
if rand > 1
|
91
|
+
setCornerRadius(1.0)
|
92
|
+
setFrame([[0,0],[0,0]])
|
93
|
+
setTransform(nil)
|
94
|
+
setMasksToBounds(0)
|
95
|
+
setShadowOffset(0)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# merge definitions for 'frame' into one.
|
100
|
+
#
|
101
|
+
# To support 'extends' more nicely it's convenient to split left, top, width
|
102
|
+
# and height out of frame. Unfortunately that means we have to write ugly
|
103
|
+
# code like this to reassemble them into what the user actually meant.
|
104
|
+
#
|
105
|
+
# WARNING: this method *mutates* its parameter.
|
106
|
+
#
|
107
|
+
# @param Hash
|
108
|
+
# @return Hash
|
109
|
+
def clean_properties!(properties)
|
110
|
+
return unless [:frame, :left, :top, :width, :height].any?(&properties.method(:key?))
|
111
|
+
|
112
|
+
frame = properties.delete(:frame) || self.frame
|
113
|
+
|
114
|
+
frame[0][0] = properties.delete(:left) || frame[0][0]
|
115
|
+
frame[0][1] = properties.delete(:top) || frame[0][1]
|
116
|
+
frame[1][0] = properties.delete(:width) || frame[1][0]
|
117
|
+
frame[1][1] = properties.delete(:height) || frame[1][1]
|
118
|
+
|
119
|
+
properties[:frame] = frame
|
120
|
+
properties
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
Binary file
|
Binary file
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# Railsifiying mobile development
|
2
|
+
|
3
|
+
** This is a rough idea, which may have lots of holes, please comment on it, post issues, or fork. **
|
4
|
+
|
5
|
+
|
6
|
+
It's time we take control of iOS app development, because it's clunky,
|
7
|
+
slow, not obvious and because we love Ruby & Rails.
|
8
|
+
|
9
|
+
We can't have "just" styling, it's not a full solution we need a framework now
|
10
|
+
more then ever.
|
11
|
+
|
12
|
+
With that said, I believe Rails provides a solid MVC. It can help hide or augment
|
13
|
+
may of the typical problems with iOS development.
|
14
|
+
|
15
|
+
|
16
|
+
Directory structure:
|
17
|
+
|
18
|
+
.teacup
|
19
|
+
|____app
|
20
|
+
| |____config
|
21
|
+
| | |____application.rb
|
22
|
+
| | |____boot.rb
|
23
|
+
| | |____initializers
|
24
|
+
| | | |____twitter.rb
|
25
|
+
| |____controllers
|
26
|
+
| | |____events_controller.rb
|
27
|
+
| | |____venues_controller.rb
|
28
|
+
| |____db
|
29
|
+
| | |____migrations
|
30
|
+
| | | |____20120514201043_create_events.rb
|
31
|
+
| | | |____20120514201044_add_price_to_events.rb
|
32
|
+
| | | |____20120514201045_create_venues.rb
|
33
|
+
| | |____README.md
|
34
|
+
| | |____schema.rb
|
35
|
+
| |____models
|
36
|
+
| | |____event.rb
|
37
|
+
| | |____venue.rb
|
38
|
+
| |____views
|
39
|
+
| | |____events
|
40
|
+
| | | |____edit.ipad.rb
|
41
|
+
| | | |____edit.iphone.rb
|
42
|
+
| | | |____show.ipad.rb
|
43
|
+
| | | |____show.iphone.rb
|
44
|
+
| | |____venues
|
45
|
+
|____README.md
|
@@ -0,0 +1 @@
|
|
1
|
+
# Idea: framework runtime, starts to load the application, load initializers, run migrations, etc
|
@@ -0,0 +1 @@
|
|
1
|
+
# Idea: framework booting before application (if it's needed at all)
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class EventsController < Controller
|
2
|
+
|
3
|
+
# Idea: Need to think about this more, but in lue of generators, we could have
|
4
|
+
# over-rideable defaults based on a responds_to/actions
|
5
|
+
# Idea: This would also configure what actions a controller can respond to / route to (do we need routes?)
|
6
|
+
responds_to :only => [:new, :create, :edit, :update, :destroy, :index, :show] # default options
|
7
|
+
|
8
|
+
def show(id)
|
9
|
+
@event = Event.find(id)
|
10
|
+
render :show
|
11
|
+
end
|
12
|
+
|
13
|
+
def edit(id)
|
14
|
+
@event = Event.find(id)
|
15
|
+
end
|
16
|
+
|
17
|
+
def update(id)
|
18
|
+
# Note: How would this conflict with the previously set @event, should something be passed? augment params?
|
19
|
+
@event = Event.find(id)
|
20
|
+
@event.update_attributes(
|
21
|
+
# 1. data from the current/old view?
|
22
|
+
# 2. autosave was false
|
23
|
+
:price => self.view.price
|
24
|
+
)
|
25
|
+
# render :edit # Idea: (self.view is loaded)
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# DB
|
2
|
+
|
3
|
+
## CoreDataAdapter
|
4
|
+
|
5
|
+
Rationale: CD is widely used in iOS projects and may be useful for simpler
|
6
|
+
integration with UI.
|
7
|
+
|
8
|
+
## SqlLiteAdapter
|
9
|
+
|
10
|
+
This would be responsible for handling CoreData+Sqlite backend.
|
11
|
+
|
12
|
+
## RemoteAdapter
|
13
|
+
|
14
|
+
This would be responsible for handling remote data sources (initially just JSON).
|
15
|
+
|
16
|
+
* A RemoteAdapter would be migration-less, APIs would need to be backwards compatible or be versioned, see endpoint in `models/event.rb`
|