studio54 0.0.5

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.
Files changed (76) hide show
  1. data/Gemfile +31 -0
  2. data/Gemfile.lock +68 -0
  3. data/LICENSE +25 -0
  4. data/README.md +0 -0
  5. data/Rakefile +56 -0
  6. data/VERSION +5 -0
  7. data/app/controllers/users_controller.rb +29 -0
  8. data/app/models/post.rb +4 -0
  9. data/app/models/user.rb +46 -0
  10. data/bare/Gemfile +31 -0
  11. data/bare/Rakefile +28 -0
  12. data/bare/config.ru +9 -0
  13. data/bare/config/app_tie.rb +12 -0
  14. data/bare/config/db.rb +20 -0
  15. data/bare/config/db_connect.rb +19 -0
  16. data/bare/config/environment.rb +17 -0
  17. data/bare/config/sinatra.rb +36 -0
  18. data/bare/config/studio54_tie.rb +2 -0
  19. data/bare/dance.rb +11 -0
  20. data/bare/lib/after_filters.rb +7 -0
  21. data/bare/lib/before_filters.rb +7 -0
  22. data/bare/lib/helpers.rb +5 -0
  23. data/bare/public/index.rhtml +1 -0
  24. data/bare/public/layout.rhtml +12 -0
  25. data/bare/static/css/base.css +86 -0
  26. data/bare/static/css/yui_reset.css +30 -0
  27. data/bare/test/helpers.rb +8 -0
  28. data/bare/test/suite.rb +13 -0
  29. data/bin/studio54 +65 -0
  30. data/config.ru +9 -0
  31. data/config/app_tie.rb +8 -0
  32. data/config/db.rb +20 -0
  33. data/config/db_connect.rb +19 -0
  34. data/config/environment.rb +17 -0
  35. data/config/sinatra.rb +36 -0
  36. data/dance.rb +80 -0
  37. data/ideas +0 -0
  38. data/lib/after_filters.rb +7 -0
  39. data/lib/base.rb +29 -0
  40. data/lib/before_filters.rb +30 -0
  41. data/lib/helpers.rb +24 -0
  42. data/lib/lazy_controller.rb +87 -0
  43. data/lib/lazy_record.rb +395 -0
  44. data/lib/partials.rb +19 -0
  45. data/lib/studio54.rb +14 -0
  46. data/lib/vendor.rb +23 -0
  47. data/public/_partial_test.rhtml +4 -0
  48. data/public/all.rhtml +7 -0
  49. data/public/form.rhtml +11 -0
  50. data/public/index.rhtml +6 -0
  51. data/public/layout.rhtml +15 -0
  52. data/public/partial_test.rhtml +6 -0
  53. data/public/test_find_by.rhtml +4 -0
  54. data/rack/cache/body/ec/b48431757330e446c58d88e317574ef0eca2e9 +0 -0
  55. data/rack/cache/meta/25/3a7a342a66b79119b7b8cb34bda89978f9e606 +0 -0
  56. data/rack/cache/meta/c1/34c9b7112c7d884220d6ee9a8e43ec69d2ea6e +0 -0
  57. data/static/css/base.css +86 -0
  58. data/static/css/yui_reset.css +30 -0
  59. data/static/hello.html +1 -0
  60. data/studio54.gemspec +25 -0
  61. data/tags +149 -0
  62. data/test/email.rb +8 -0
  63. data/test/environment.rb +28 -0
  64. data/test/helpers.rb +8 -0
  65. data/test/integration/index_test.rb +41 -0
  66. data/test/integration/partial_test.rb +50 -0
  67. data/test/mail/email_test.rb +14 -0
  68. data/test/rack/helpers.rb +23 -0
  69. data/test/suite.rb +8 -0
  70. data/test/unit/associations_test.rb +33 -0
  71. data/test/unit/callbacks_test.rb +16 -0
  72. data/test/unit/database_test.rb +73 -0
  73. data/test/unit/model_introspection_test.rb +44 -0
  74. data/test/unit/serialization_test.rb +19 -0
  75. data/test/unit/validations_test.rb +73 -0
  76. metadata +165 -0
data/ideas ADDED
File without changes
@@ -0,0 +1,7 @@
1
+ module Studio54
2
+ class Dancefloor
3
+ after do
4
+ end
5
+ end
6
+ end
7
+
@@ -0,0 +1,29 @@
1
+ module Studio54
2
+ class Base
3
+ include Config::Environment
4
+ # wrap Sinatra's scopes for convenience
5
+ # within the controller and model
6
+ cattr_accessor :app_class
7
+ cattr_accessor :app_instance
8
+
9
+ # All models are not required by default. These are helper
10
+ # methods to aid doing it manually
11
+ def self.require_models(*models)
12
+ models.each do |m|
13
+ require File.join(MODELSDIR, m.to_s)
14
+ end
15
+ end
16
+
17
+ class << self
18
+ alias_method :require_model, :require_models
19
+ end
20
+
21
+ def self.require_all_models
22
+ Dir.glob(File.join(MODELSDIR, '*')).each do |m_file|
23
+ require m_file
24
+ end
25
+ end
26
+
27
+ end
28
+ end
29
+
@@ -0,0 +1,30 @@
1
+ module Studio54
2
+ class Dancefloor
3
+ # wraps Dancefloor class scope (app) for convenience
4
+ Base.app_class = self
5
+
6
+ before do
7
+ load 'config/db_connect.rb' if self.class.environment ==
8
+ :development
9
+ # wraps Dancefloor instance scope (request) for convenience
10
+ Base.app_instance = self
11
+
12
+ # if using shotgun, create a custom log format
13
+ !settings.shotgun || begin
14
+ req = request
15
+ logger.class_eval do
16
+ cattr_accessor :format
17
+ self.__send__ :format=, <<-HTML
18
+ #{Time.now}
19
+ #{req.request_method} #{req.fullpath}
20
+ Content-Length: #{req.content_length}
21
+ Params:#{req.params}
22
+ HTML
23
+ end
24
+ logger.info logger.format
25
+ end
26
+
27
+ end
28
+ end
29
+ end
30
+
@@ -0,0 +1,24 @@
1
+ class Studio54::Dancefloor
2
+ helpers do
3
+ include Rack::Utils
4
+ # have to explicitly use h(...) to escape html
5
+ alias_method :h, :escape_html
6
+ end
7
+ end
8
+
9
+ module Sinatra
10
+ class Response
11
+
12
+ def set_content_length!
13
+ self["Content-Length"] =
14
+ self.body.inject(0) {|a, l| a += l.length}
15
+ end
16
+
17
+ def send(status=200)
18
+ set_content_length!
19
+ [status, self.headers, self.body]
20
+ end
21
+
22
+ end
23
+ end
24
+
@@ -0,0 +1,87 @@
1
+ class LazyController < Studio54::Base
2
+
3
+ class << self
4
+
5
+ def inherited(base)
6
+ base.extend ActiveModel::Callbacks
7
+ end
8
+
9
+ def app_class_eval(&block)
10
+ self.app_class.class_eval &block
11
+ end
12
+
13
+ def app_instance_eval(&block)
14
+ self.app_instance.instance_eval &block
15
+ end
16
+
17
+ ["before_action", "after_action", "around_action"].each do |m|
18
+ m =~ /\A(.*?)_/
19
+ type = $1
20
+ class_eval <<RUBY, __FILE__, __LINE__ + 1
21
+ def #{m}(action, &block)
22
+ define_callbacks ('old_' + action.to_s)
23
+ set_callback ('old_' + action.to_s), :#{type}, &block
24
+ define_singleton_method "method_added" do |action_name|
25
+ matchstring = Regexp.new('^' + action.to_s + '$')
26
+ if matchstring.match action_name
27
+ alias_method ('old_' + action.to_s).intern, action
28
+ remove_method action
29
+ define_method ("proxy_" + action.to_s) do |*args, &block|
30
+ run_callbacks ('old_' + action.to_s) do
31
+ __send__ ('old_' + action.to_s).intern , *args, &block
32
+ end
33
+ end
34
+ define_method "method_missing" do |method, *args, &block|
35
+ if matchstring.match method
36
+ __send__ ("proxy_" + method.to_s), *args, &block
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ RUBY
43
+ end
44
+ end
45
+
46
+ # included in Dancefloor
47
+ module Routable
48
+ def self.included(base)
49
+ base.__send__ :use, Rack::Flash
50
+ base.__send__ :helpers, Sinatra::Partials
51
+ end
52
+
53
+ # Db is a constant in Studio54 scope
54
+ include ::Studio54
55
+ include ::Studio54::Config::Environment
56
+
57
+ def controller(c_name, c_action, params={})
58
+ require File.join(CONTROLLERSDIR, "#{c_name}_controller")
59
+ require File.join(MODELSDIR, c_name[0...-1])
60
+ begin
61
+ controller = self.class.const_get("#{c_name.capitalize}Controller")
62
+ controller_inst = controller.new
63
+ result = if params.blank?
64
+ controller_inst.__send__(c_action)
65
+ else
66
+ controller_inst.__send__(c_action, params)
67
+ end
68
+ controller_inst.instance_variables.each do |ivar|
69
+ # establish non block-local scope
70
+ ivar_value = nil
71
+ controller_inst.instance_eval do
72
+ ivar_value = instance_variable_get ivar
73
+ end
74
+ # self here is the instance of the application
75
+ instance_variable_set ivar, ivar_value
76
+ end
77
+ ensure
78
+ Db.conn.disconnect if Db.conn.kind_of? DBI::DatabaseHandle and
79
+ Db.conn.connected?
80
+ end
81
+ result
82
+ end
83
+
84
+ end
85
+
86
+ end
87
+
@@ -0,0 +1,395 @@
1
+ class LazyRecord < Studio54::Base
2
+ include ::Studio54
3
+ # ActiveModel::Callbacks Base.class_eval {includes ActiveSupport::Callbacks}
4
+ extend ActiveModel::Callbacks
5
+ include ActiveModel::Serializers::JSON
6
+ include ActiveModel::Serializers::Xml
7
+ extend ActiveModel::Naming
8
+ extend ActiveModel::Translation
9
+ include ActiveModel::Validations
10
+
11
+ RecordNotFound = Class.new(StandardError)
12
+ AssociationNotFound = Class.new(StandardError)
13
+
14
+ def self.inherited(base)
15
+ base.class_eval do
16
+ cattr_accessor :primary_key
17
+ attr_reader :errors
18
+ end
19
+ end
20
+
21
+ # if parameters are given, builds the model
22
+ # object with the attributes from the given
23
+ # parameters
24
+ def initialize(params=nil)
25
+ unless params.nil?
26
+ self.build_from_params!(params)
27
+ end
28
+ @errors = ActiveModel::Errors.new(self)
29
+ end
30
+
31
+ # used to keep track of all table attributes
32
+ def self.tbl_attr_accessor *fields
33
+ fields.each do |f|
34
+ self.attributes << f.to_s unless self.attributes.include? f.to_s
35
+ end
36
+ self.__send__ :attr_accessor, *fields
37
+ end
38
+
39
+ def self.attributes
40
+ @attributes ||= []
41
+ end
42
+
43
+ def self.all_attributes
44
+ @all_attributes ||= begin
45
+ attrs = @attributes.dup
46
+ attrs.unshift primary_key
47
+ end
48
+ end
49
+
50
+ def self.nested_attributes
51
+ @nested_attributes ||= []
52
+ end
53
+
54
+ def self.belongs_to_attributes
55
+ @belongs_to_attributes ||= []
56
+ end
57
+
58
+ private
59
+
60
+ # Defines the accessor(s) and makes sure the other model includes
61
+ # an appropriate association as well.
62
+ #
63
+ # The name of the model can be made explicit if it's different
64
+ # from the default (chop the trailing 's' off from the model name).
65
+ #
66
+ # Example:
67
+ # has_many :tags, {:through => 'tags_articles', :fk => 'tagid'}
68
+ #
69
+ # default
70
+ # =======
71
+ #
72
+ # has_many :tags
73
+ #
74
+ # is equivalent to
75
+ #
76
+ # has_many :tags, {:through => 'tags_articles', :fk => 'tag_id'}
77
+ def self.has_many model, options={}
78
+ # @join_tables:
79
+ # Used with LR#build_associated, to find out what the join table and
80
+ # the foreign key are. If not found, it generates default ones by the
81
+ # heuristic explained above the LR#build_associated method declaration.
82
+ @join_tables ||= {}
83
+ unless @join_tables[model]
84
+ @join_tables[model] = {}
85
+ end
86
+ if through = options[:through]
87
+ @join_tables[model][:through] = through
88
+ end
89
+ if fk = options[:fk]
90
+ @join_tables[model][:fk] = fk
91
+ end
92
+
93
+ model_string = model.to_s.singularize
94
+ self.require_model model_string
95
+ model_klass = Object.const_get model_string.camelize
96
+ ivar_name = model
97
+
98
+ # Make sure associated model #belongs_to this class.
99
+ unless model_klass.belongs_to_attributes.include? self.name.
100
+ tableize
101
+ raise AssociationNotFound.new "#{model_klass} doesn't #belong_to " \
102
+ "#{self.name}"
103
+ end
104
+ class_eval do
105
+ define_method ivar_name do
106
+ instance_variable_get("@#{ivar_name}") || []
107
+ end
108
+ attr_writer ivar_name
109
+ self.nested_attributes << ivar_name
110
+ end
111
+ end
112
+
113
+ class << self
114
+ # has_one has the same implementation as has_many
115
+ alias_method :has_one, :has_many
116
+ end
117
+
118
+ # barely does anything, just works with has_many
119
+ def self.belongs_to *models
120
+ models.each do |m|
121
+ self.belongs_to_attributes << m.to_s
122
+ end
123
+ end
124
+
125
+ def self.attr_primary(*fields)
126
+ if fields.length == 1
127
+ self.primary_key = fields[0].to_s != "" ? fields[0].to_s : nil
128
+ else
129
+ self.primary_key = fields.map {|f| f.to_s }
130
+ end
131
+ class_eval do
132
+ fields.each do |f|
133
+ attr_accessor f unless f.nil?
134
+ end
135
+ end
136
+ end
137
+
138
+ # associated table name, by default is just to add an 's' to the model
139
+ # name
140
+ def self.assoc_table_name=( tblname=self.name.tableize )
141
+ self.__send__ :cattr_accessor, :table_name
142
+ self.table_name = tblname.to_s
143
+ end
144
+
145
+ public
146
+
147
+ # meant for internal use
148
+ def build_from_params!(params)
149
+ params.each do |k, v|
150
+ self.__send__("#{k}=".intern, v)
151
+ end
152
+ end
153
+
154
+ # Have to implement Model#attributes to play nice
155
+ # with ActiveModel serializers
156
+ def attributes(options={})
157
+ opts = {:include_pk => true}.merge options
158
+ if opts[:include_pk]
159
+ attrs = self.class.all_attributes
160
+ else
161
+ attrs = self.class.attributes
162
+ end
163
+ {}.tap do |h|
164
+ attrs.each do |a_name|
165
+ h[a_name] = instance_variable_get "@#{a_name}"
166
+ end
167
+ end
168
+ end
169
+
170
+ # save current model instance into database
171
+ def save
172
+ return unless valid?
173
+ sql = "INSERT INTO #{self.class.table_name} ("
174
+ fields = self.class.attributes
175
+ sql += fields.join(', ') + ') VALUES ('
176
+ fields.each {|f| sql += '?, '}
177
+ sql = sql[0...-2] + ')'
178
+ values = fields.map do |f|
179
+ ivar = instance_variable_get "@#{f}"
180
+ if ivar.nil?
181
+ "NULL"
182
+ else
183
+ ivar
184
+ end
185
+ end
186
+ result = nil
187
+ self.class.db_try do
188
+ result = Db.conn.execute sql, *values
189
+ end
190
+ result ? true : false
191
+ end
192
+
193
+ # delete the current model instance from the database
194
+ def destroy(options={})
195
+ if options[:where]
196
+ else
197
+ sql = "DELETE FROM #{self.class.table_name} WHERE " \
198
+ "#{self.primary_key} = ?"
199
+ result = nil
200
+ self.class.db_try do
201
+ result = Db.conn.execute sql,
202
+ instance_variable_get("@#{self.primary_key}")
203
+ end
204
+ result
205
+ end
206
+ end
207
+
208
+ # update the current model instance in the database
209
+ def update_attributes(params, extra_where={})
210
+ values = []
211
+ key = self.primary_key
212
+ id = params.delete key
213
+ if extra_where.blank?
214
+ sql = "UPDATE #{self.class.table_name} SET "
215
+ params.each do |k,v|
216
+ sql += "#{k} = ?, "
217
+ values << v
218
+ end
219
+ sql = sql[0...-2]
220
+ sql += " WHERE #{key} = ?"
221
+ values << id
222
+ else
223
+ end
224
+ res = nil
225
+ self.class.db_try do
226
+ res = Db.conn.execute sql, *values
227
+ end
228
+ res
229
+ end
230
+
231
+ # This method uses the model instance's class::has_many()
232
+ # method to determine what the join table is called. The
233
+ # default join table name that this method uses if no
234
+ # options were given to Model::has_many() (see Model::has_many()
235
+ # for the passable options) is the following:
236
+ #
237
+ # the tbl argument (for example, :tags), concatenated with
238
+ # `self`'s class's table name (for example, 'articles')
239
+ # to make 'tags_articles'
240
+ #
241
+ # The default foreign key uses a similar heuristic. For the
242
+ # example above, it would be 'tag_id', because the given
243
+ # table name is 'tags', and the singular is 'tag'. This
244
+ # is then concatenated with '_id'
245
+ #
246
+ # To override the defaults, provide options to Model::has_many()
247
+ def build_associated tbl
248
+ self_tbl = self.class.table_name
249
+ through = if _through = self.class.
250
+ instance_variable_get("@join_tables")[tbl][:through]
251
+ _through
252
+ else
253
+ "#{tbl}_#{self_tbl}"
254
+ end
255
+ fk = if _fk = self.class.
256
+ instance_variable_get("@join_tables")[tbl][:fk]
257
+ _fk
258
+ else
259
+ "#{tbl.to_s.singularize}_id"
260
+ end
261
+
262
+ tbl_model_name = tbl.to_s.singularize.camelize
263
+ begin
264
+ tbl_model = Object.const_get tbl_model_name
265
+ rescue NameError
266
+ retry if require "app/models/#{tbl_model_name.downcase}"
267
+ end
268
+ pk = self.class.primary_key
269
+ id = __send__ pk
270
+ sql = "SELECT * FROM #{tbl} INNER JOIN #{through} ON #{tbl}.#{tbl_model.primary_key} = " \
271
+ "#{through}.#{fk} WHERE #{through}.#{self_tbl.singularize + '_id'} = ?"
272
+ puts sql
273
+ res = nil
274
+ self.class.db_try do
275
+ res = Db.conn.execute sql, id
276
+ end
277
+ objs = tbl_model.build_from res
278
+ p objs
279
+ objs = Array.wrap(objs) unless Array === objs
280
+ __send__("#{tbl}=", __send__(tbl) + objs) unless objs.blank?
281
+ end
282
+
283
+ def self.db_try
284
+ begin
285
+ yield
286
+ rescue DBI::DatabaseError
287
+ @retries ||= 0; @retries += 1
288
+ if @retries == 1
289
+ load 'config/db_connect.rb'
290
+ retry
291
+ else
292
+ raise
293
+ end
294
+ end
295
+ end
296
+
297
+ # id is the primary key of the table, and does
298
+ # not need to be named 'id' in the table itself
299
+ # TODO take into account other dbms's, this only
300
+ # works w/ mysql
301
+ def self.find(id)
302
+ sql = "SELECT * FROM #{self.table_name} WHERE #{self.primary_key} = ?"
303
+ res = nil
304
+ db_try do
305
+ res = Db.conn.execute sql, id
306
+ end
307
+ build_from res
308
+ end
309
+
310
+ def self.find_by(hash, options={})
311
+ opts = {:conjunction => 'AND'}.merge options
312
+ conj = opts[:conjunction]
313
+ sql = "SELECT * FROM #{self.table_name} WHERE "
314
+ values = []
315
+ hash.each do |k, v|
316
+ sql += "#{k} = ? #{conj} "
317
+ values << v
318
+ end
319
+ case conj
320
+ when 'AND'
321
+ sql = sql[0...-4]
322
+ when 'OR'
323
+ sql = sql[0...-3]
324
+ else
325
+ raise "conjunction in sql condition (WHERE) must be one of AND, OR"
326
+ end
327
+ res = nil
328
+ db_try do
329
+ res = Db.conn.execute sql, *values
330
+ end
331
+ build_from res
332
+ end
333
+
334
+ def self.all
335
+ sql = "SELECT * FROM #{self.table_name}"
336
+ res = nil
337
+ db_try do
338
+ res = Db.conn.execute(sql)
339
+ end
340
+ build_from res, :always_return_array => true
341
+ end
342
+
343
+ private
344
+
345
+ # meant for internal use
346
+ def self.build_from(resultset, options={})
347
+ opts = {:always_return_array => false}.merge options
348
+ test_resultset resultset
349
+ model_instances = [].tap do |m|
350
+ resultset.fetch_hash do |h|
351
+ model = self.new
352
+ model.build_from_params! h
353
+ m << model
354
+ end
355
+ resultset.finish
356
+ end
357
+ model_instances.length == 1 && !opts[:always_return_array] ?
358
+ model_instances[0] : model_instances
359
+ end
360
+
361
+ # meant for internal use
362
+ def self.test_resultset(res)
363
+ if res.blank?
364
+ raise RecordNotFound.new "Bad resultset #{res}"
365
+ end
366
+ end
367
+
368
+ public
369
+
370
+ class << self
371
+ def method_missing(method, *args, &block)
372
+ if method =~ %r{find_by_(.*)}
373
+ h_args = {$1 => args[0]}
374
+ return __send__ :find_by, h_args, &block
375
+ end
376
+
377
+ if method =~ %r{table_name}
378
+ return __send__ :assoc_table_name=
379
+ end
380
+
381
+ super
382
+ end
383
+ end
384
+
385
+ end
386
+
387
+ # Arrays should respond to build_associated just like models.
388
+ class Array
389
+ def build_associated tbl
390
+ each do |m|
391
+ m.__send__ :build_associated, tbl
392
+ end
393
+ end
394
+ end
395
+