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.
- data/Gemfile +31 -0
- data/Gemfile.lock +68 -0
- data/LICENSE +25 -0
- data/README.md +0 -0
- data/Rakefile +56 -0
- data/VERSION +5 -0
- data/app/controllers/users_controller.rb +29 -0
- data/app/models/post.rb +4 -0
- data/app/models/user.rb +46 -0
- data/bare/Gemfile +31 -0
- data/bare/Rakefile +28 -0
- data/bare/config.ru +9 -0
- data/bare/config/app_tie.rb +12 -0
- data/bare/config/db.rb +20 -0
- data/bare/config/db_connect.rb +19 -0
- data/bare/config/environment.rb +17 -0
- data/bare/config/sinatra.rb +36 -0
- data/bare/config/studio54_tie.rb +2 -0
- data/bare/dance.rb +11 -0
- data/bare/lib/after_filters.rb +7 -0
- data/bare/lib/before_filters.rb +7 -0
- data/bare/lib/helpers.rb +5 -0
- data/bare/public/index.rhtml +1 -0
- data/bare/public/layout.rhtml +12 -0
- data/bare/static/css/base.css +86 -0
- data/bare/static/css/yui_reset.css +30 -0
- data/bare/test/helpers.rb +8 -0
- data/bare/test/suite.rb +13 -0
- data/bin/studio54 +65 -0
- data/config.ru +9 -0
- data/config/app_tie.rb +8 -0
- data/config/db.rb +20 -0
- data/config/db_connect.rb +19 -0
- data/config/environment.rb +17 -0
- data/config/sinatra.rb +36 -0
- data/dance.rb +80 -0
- data/ideas +0 -0
- data/lib/after_filters.rb +7 -0
- data/lib/base.rb +29 -0
- data/lib/before_filters.rb +30 -0
- data/lib/helpers.rb +24 -0
- data/lib/lazy_controller.rb +87 -0
- data/lib/lazy_record.rb +395 -0
- data/lib/partials.rb +19 -0
- data/lib/studio54.rb +14 -0
- data/lib/vendor.rb +23 -0
- data/public/_partial_test.rhtml +4 -0
- data/public/all.rhtml +7 -0
- data/public/form.rhtml +11 -0
- data/public/index.rhtml +6 -0
- data/public/layout.rhtml +15 -0
- data/public/partial_test.rhtml +6 -0
- data/public/test_find_by.rhtml +4 -0
- data/rack/cache/body/ec/b48431757330e446c58d88e317574ef0eca2e9 +0 -0
- data/rack/cache/meta/25/3a7a342a66b79119b7b8cb34bda89978f9e606 +0 -0
- data/rack/cache/meta/c1/34c9b7112c7d884220d6ee9a8e43ec69d2ea6e +0 -0
- data/static/css/base.css +86 -0
- data/static/css/yui_reset.css +30 -0
- data/static/hello.html +1 -0
- data/studio54.gemspec +25 -0
- data/tags +149 -0
- data/test/email.rb +8 -0
- data/test/environment.rb +28 -0
- data/test/helpers.rb +8 -0
- data/test/integration/index_test.rb +41 -0
- data/test/integration/partial_test.rb +50 -0
- data/test/mail/email_test.rb +14 -0
- data/test/rack/helpers.rb +23 -0
- data/test/suite.rb +8 -0
- data/test/unit/associations_test.rb +33 -0
- data/test/unit/callbacks_test.rb +16 -0
- data/test/unit/database_test.rb +73 -0
- data/test/unit/model_introspection_test.rb +44 -0
- data/test/unit/serialization_test.rb +19 -0
- data/test/unit/validations_test.rb +73 -0
- metadata +165 -0
data/ideas
ADDED
File without changes
|
data/lib/base.rb
ADDED
@@ -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
|
+
|
data/lib/helpers.rb
ADDED
@@ -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
|
+
|
data/lib/lazy_record.rb
ADDED
@@ -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
|
+
|