Module: Waxx::View
- Defined in:
- waxx/view.rb
Overview
A Waxx::View is like a database view. You define the primary object and related tables and fields on the view and it handles some routine processes for you. You can also just use it as a container for specialized business logic or complicated queries.
Example usage:
“` module App::Usr::List
extend Waxx::View
extend self
# Define what layouts to allow
as :json
# Define what fields are in the view
has(
# Fields that are in the 'usr' table
:id,
:usr_name,
:last_login_date,
:last_login_host,
:failed_login_count,
# Fields that are in the 'person' table. Relationships are defined in App::Usr.has(...)
"person_id: person.id", # This column is accessible as 'person_id' on this view
"person.first_name",
"person.last_name",
)
end “`
This view definition will provide you with the following functionality:
“` App::Usr::List.get(x) # Executes the following SQL: SELECT usr.id, usr.usr_name, usr.last_login_date, usr.last_login_host, usr.failed_login_count,
person.id AS person_id, person.first_name, person.last_name
FROM usr LEFT JOIN person ON usr.id = person.id # And returns a PG::Result (If you are using the PG database connector) “`
Instance Attribute Summary collapse
-
#columns ⇒ Object
readonly
A hash of columns (See Waxx::Pg.has).
-
#joins ⇒ Object
readonly
A hash of name: join_sql.
-
#matches ⇒ Object
readonly
How to search the view by specific field.
-
#object ⇒ Object
readonly
The parent (primary) object.
-
#order_by ⇒ Object
readonly
The default order of the results.
-
#orders ⇒ Object
readonly
A hash of how you can sort this view.
-
#relations ⇒ Object
readonly
A hash of related tables.
-
#searches ⇒ Object
readonly
How to search the view by the “q” parameter.
-
#table ⇒ Object
readonly
The table name of the primary object.
Instance Method Summary collapse
-
#[](c) ⇒ Object
Get a column on the view.
-
#as(*views) ⇒ Object
Autogenerate the modules to do standard layouts like Json, Csv, or Tab.
-
#build_where(x, args: {}, matches: @matches, searches: @searches) ⇒ Object
Automatically build the where clause of SQL based on the parameters passed in and the definition of matches and searches.
-
#debug(str, level = 3) ⇒ Object
A shorcut to Waxx.debug.
-
#default_order(ord) ⇒ Object
Set the default order.
-
#delete(x, id) ⇒ Object
Delete a record by ID (primary key of the primary object).
-
#get(x, where: nil, having: nil, order: nil, limit: nil, offset: nil, args: {}, &blk) ⇒ Object
Override this method in a view to change params.
-
#get_by_id(x, id) ⇒ Object
(also: #by_id)
Get a single record of the view based on the primary key of the primary object.
-
#has(*cols) ⇒ Object
Columnas on a view can be defined in multiple ways:.
-
#init(tbl: nil, cols: nil, layouts: nil) ⇒ Object
Initialize a view.
-
#joins_to_sql ⇒ Object
Turn the @joins attribute into SQL for the JOIN clause.
-
#match_in(*cols) ⇒ Object
An array of columns to match in when passed in as params.
-
#not_found(x, data: {}, message: {type: "NotFound", message:"The record you requested was not found."}, as: x.ext) ⇒ Object
Send a not found message back to the client using the appropriate layout (json, csv, etc).
-
#parse_col(str) ⇒ Object
Parse a column (internal method used by col).
-
#put_post(x, id, data, args: nil, returning: nil) ⇒ Object
(also: #post, #put)
Save data.
-
#render(x, data, message: {}, as: x.ext, meth: x.meth) ⇒ Object
Render the view using the layout for meth and as.
-
#run(x, id: nil, data: nil, where: nil, having: nil, order: nil, limit: nil, offset: nil, message: {}, as: x.ext, meth: x.meth, args: {}) ⇒ Object
(also: #view)
Gets the data for the view and displays it.
-
#search_in(*cols) ⇒ Object
Any array of columns to automatically search in using the “q” parameter.
-
#string_to_col(str) ⇒ Object
Column defined as a string in the format: name:foreign_table.foreign_col Converted to SQL: foreign_table.foreign_col AS name Also adds entries in the @relations hash.
Instance Attribute Details
#columns ⇒ Object (readonly)
A hash of columns (See Waxx::Pg.has)
52 53 54 |
# File 'waxx/view.rb', line 52 def columns @columns end |
#joins ⇒ Object (readonly)
A hash of name: join_sql. Normally set automatically when the columns are parsed.
54 55 56 |
# File 'waxx/view.rb', line 54 def joins @joins end |
#matches ⇒ Object (readonly)
How to search the view by specific field
58 59 60 |
# File 'waxx/view.rb', line 58 def matches @matches end |
#object ⇒ Object (readonly)
The parent (primary) object. For example in App::Usr::List, App::Usr is the @object
48 49 50 |
# File 'waxx/view.rb', line 48 def object @object end |
#order_by ⇒ Object (readonly)
The default order of the results
62 63 64 |
# File 'waxx/view.rb', line 62 def order_by @order_by end |
#orders ⇒ Object (readonly)
A hash of how you can sort this view
64 65 66 |
# File 'waxx/view.rb', line 64 def orders @orders end |
#relations ⇒ Object (readonly)
A hash of related tables. Normally set automatically when the columns are parsed.
56 57 58 |
# File 'waxx/view.rb', line 56 def relations @relations end |
#searches ⇒ Object (readonly)
How to search the view by the “q” parameter
60 61 62 |
# File 'waxx/view.rb', line 60 def searches @searches end |
#table ⇒ Object (readonly)
The table name of the primary object
50 51 52 |
# File 'waxx/view.rb', line 50 def table @table end |
Instance Method Details
#[](c) ⇒ Object
Get a column on the view
“` App::Usr::Record => :label=>“User Name”, :table=>:usr, :column=>:usr_name, :views=>[App::Usr::Record, App::Usr::List, App::Usr::Signup] “`
91 92 93 |
# File 'waxx/view.rb', line 91 def [](c) @columns[c.to_sym] end |
#as(*views) ⇒ Object
233 234 235 236 237 238 239 240 241 242 |
# File 'waxx/view.rb', line 233 def as(*views) views.each{|v| eval(" module #{name}::#{v.to_s.capitalize} extend Waxx::#{v.to_s.capitalize} extend self end ") } end |
#build_where(x, args: {}, matches: @matches, searches: @searches) ⇒ Object
Automatically build the where clause of SQL based on the parameters passed in and the definition of matches and searches.
302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 |
# File 'waxx/view.rb', line 302 def build_where(x, args: {}, matches: @matches, searches: @searches) return nil if args.nil? or args.empty? or (matches.nil? and searches.nil?) w_str = "" w_args = [] q = args/:q || x['q'] if q and searches w_str += "(" searches.each_with_index{|c, i| w_str += " OR " if i > 0 w_str += "LOWER(#{c}) like $1" } w_args << "%#{q.downcase}%" w_str += ")" end if matches matches.each_with_index{|c, i| next if (x/c).to_s == "" and (args/c).to_s == "" w_str += " AND " if w_str != "" col = self[c.to_sym] w_str += "#{c} #{col[:match] || "="} $#{w_args.size + 1}" w_args << (args/c || x/c) } end [w_str, w_args] end |
#debug(str, level = 3) ⇒ Object
A shorcut to Waxx.debug
386 387 388 |
# File 'waxx/view.rb', line 386 def debug(str, level=3) Waxx.debug(str, level) end |
#default_order(ord) ⇒ Object
Set the default order. Order is a key of the field name. Use _name to sort descending.
258 259 260 |
# File 'waxx/view.rb', line 258 def default_order(ord) @order_by = ord end |
#delete(x, id) ⇒ Object
Delete a record by ID (primary key of the primary object)
363 364 365 |
# File 'waxx/view.rb', line 363 def delete(x, id) @object.delete(x, id) end |
#get(x, where: nil, having: nil, order: nil, limit: nil, offset: nil, args: {}, &blk) ⇒ Object
Override this method in a view to change params
330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 |
# File 'waxx/view.rb', line 330 def get(x, where:nil, having:nil, order:nil, limit:nil, offset:nil, args:{}, &blk) where ||= build_where(x, args: args) order ||= args/:order || @order_by limit ||= args/:limit offset ||= args/:offset @object.get(x, view: self, where: where, joins: joins_to_sql(), having: having, order: order, limit: limit, offset: offset ) end |
#get_by_id(x, id) ⇒ Object Also known as: by_id
Get a single record of the view based on the primary key of the primary object
348 349 350 |
# File 'waxx/view.rb', line 348 def get_by_id(x, id) @object.get_by_id(x, id, view: self) end |
#has(*cols) ⇒ Object
Columnas on a view can be defined in multiple ways:
“` has(
:id, # A field in the parent object
:name, # Another field in the parent object
"company_name:company.name", # name:rel_name.col_name "name" is the name of the col in the query, rel_name is the join table as defined in object, col_name is the column in the foreign table
[:creator, {table: "person", sql_select: "first_name || ' ' || last_name", label: "Creator"}] # Array: [name, column (Hash)]
{modifier: {table: "person", sql_select: "first_name || ' ' || last_name", label: "Creator"}}
“`
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
# File 'waxx/view.rb', line 107 def has(*cols) return @columns if cols.empty? init if @object.nil? #@joins = {} @columns = {} cols.each{|c| n = col = nil case c # Get the col from the object when Symbol n = c col = @object[c] # A related col (must be defined in the related object) when String n, col = string_to_col(c) # A custom col [name, col] col is a Hash when Array n, col = c # A custom col {name: col}, col is a Hash when Hash n, col = c.to_a[0] end if col.nil? Waxx.debug "Column #{c} not defined in #{@object}." #raise "Column #{c} not defined in #{@object}." end #Waxx.debug @relations.inspect #TODO: Deal with relations that have different names than the tables col[:views] << self rescue col[:views] = [self] @columns[n.to_sym] = col } @joins ||= Hash[@relations.map{|n, r| [n, %(#{r/:join} JOIN #{r/:foreign_table} AS #{n} ON #{r/:table}.#{r/:col} = #{n}.#{r/:foreign_col})] }] end |
#init(tbl: nil, cols: nil, layouts: nil) ⇒ Object
Initialize a view. This is normally done automatically when calling `has`.
Call init if the table, object, or layouts are non-standard. You can also set the attrs directly like `@table = 'usr'`
“` tbl: The name of the table cols: Same as has layouts: The layouts to auto-generate using waxx defaults: json, csv, tab, etc. “`
76 77 78 79 80 81 82 83 |
# File 'waxx/view.rb', line 76 def init(tbl: nil, cols: nil, layouts: nil) @table = (tbl || App.table_from_class(name)).to_sym @object = App.get_const(App, @table) @relations = {} @orders = {} has(*cols) if cols as(layouts) if layouts end |
#joins_to_sql ⇒ Object
Turn the @joins attribute into SQL for the JOIN clause
213 214 215 216 |
# File 'waxx/view.rb', line 213 def joins_to_sql() return nil if @joins.nil? or @joins.empty? @joins.map{|n,v| v}.join(" ") end |
#match_in(*cols) ⇒ Object
An array of columns to match in when passed in as params
246 247 248 |
# File 'waxx/view.rb', line 246 def match_in(*cols) @matches = cols.flatten end |
#not_found(x, data: {}, message: {type: "NotFound", message:"The record you requested was not found."}, as: x.ext) ⇒ Object
Send a not found message back to the client using the appropriate layout (json, csv, etc)
380 381 382 |
# File 'waxx/view.rb', line 380 def not_found(x, data:{}, message: {type: "NotFound", message:"The record you requested was not found."}, as:x.ext) self.const_get(as.to_s.capitalize).not_found(x, data:{}, message: ) end |
#parse_col(str) ⇒ Object
Parse a column (internal method used by col)
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 |
# File 'waxx/view.rb', line 187 def parse_col(str) nam = rel = col = nil parts = str.split(/[:\.]/) case str # alias:relationship.column when /^\w+:\s*\w+\.\w+$/ nam, rel, col = str.split(/[:\.]/).map{|part| part.strip} # relationship.column when /^\w+\.\w+$/ rel, col = str.split(".") # alias:column (from primary object/table) when /^\w+:\w+$/ nam, col = str.split(":") # column (from primary object/table) when /^\w+$/ col = str else raise "Could not parse column definition in Waxx::View.parse_col (#{name}). Unknown match: #{str}." end nam = col if nam.nil? rel = @table if rel.nil? [nam, rel, col] end |
#put_post(x, id, data, args: nil, returning: nil) ⇒ Object Also known as: post, put
Save data
355 356 357 |
# File 'waxx/view.rb', line 355 def put_post(x, id, data, args:nil, returning: nil) @object.put_post(x, id, data, view: self, returning: returning) end |
#render(x, data, message: {}, as: x.ext, meth: x.meth) ⇒ Object
Render the view using the layout for meth and as
`render(x, data, as: 'json', meth: 'get')`
Uses logical defaults based on x.req
373 374 375 376 |
# File 'waxx/view.rb', line 373 def render(x, data, message: {}, as:x.ext, meth:x.meth) return App.not_found(x) unless const_defined?(as.to_s.capitalize) const_get(as.to_s.capitalize).send(meth, x, data, message: ) end |
#run(x, id: nil, data: nil, where: nil, having: nil, order: nil, limit: nil, offset: nil, message: {}, as: x.ext, meth: x.meth, args: {}) ⇒ Object Also known as: view
Gets the data for the view and displays it. This is just a shortcut method.
This is normally called from the handler method defined in Object
“` App::Usr::List.run(x) # Given a get request with the json extention, the above is a shortcut to: data = App::Usr::List.get(x) App::Usr::List::Json.get(x, data) “`
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 |
# File 'waxx/view.rb', line 273 def run(x, id:nil, data:nil, where:nil, having:nil, order:nil, limit:nil, offset:nil, message:{}, as:x.ext, meth:x.meth, args:{}) case meth.to_sym when :get, :head if data.nil? or data.empty? if id data = get_by_id(x, id) else data = get(x, where:where, having:having, order:(order||x['order']), limit:(limit||x['limit']), offset:(offset||x['offset']), args:args) end end when :put, :post, :patch data = put_post(x, id, data, args:args) when :delete delete(x, id, args:args) else raise "Unknown request method in Waxx::View.run(#{name})" end layout = const_get(as.to_s.capitalize) rescue nil return App.not_found(x, message:"No layout defined for #{as}") if not layout if layout.respond_to? meth render(x, data, message: , as: as, meth: meth) else render(x, data, message: , as: as, meth: "get") end end |
#search_in(*cols) ⇒ Object
Any array of columns to automatically search in using the “q” parameter
252 253 254 |
# File 'waxx/view.rb', line 252 def search_in(*cols) @searches = cols.flatten end |
#string_to_col(str) ⇒ Object
Column defined as a string in the format: name:foreign_table.foreign_col Converted to SQL: foreign_table.foreign_col AS name Also adds entries in the @relations hash. @relations drive the SQL join statement Joins are defined in the primary object of this view.
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 |
# File 'waxx/view.rb', line 146 def string_to_col(str) n, rel_name, col_name = parse_col(str) # Look in the primary and related objects for relations j = @object.joins/rel_name || @relations.values.map{|foreign_rel| #Waxx.debug "REL: #{foreign_rel}" o = App.get_const(App, foreign_rel/:foreign_table) #Waxx.debug o #Waxx.debug o.joins.inspect o.joins/rel_name }.compact.first #Waxx.debug "j:#{j.inspect}, n: #{n}, rel: #{rel_name}, col: #{col_name}" begin col = (App.get_const(App, j/:foreign_table)/col_name).dup col[:table] = rel_name rescue NoMethodError => e Waxx.debug "ERROR: NoMethodError: #{rel_name} does not define col: #{col_name}" raise e rescue NameError, TypeError => e Waxx.debug "ERROR: Name or Type Error: #{rel_name} does not define col: #{col_name}" raise e end begin @relations[rel_name] ||= j #(App.get_const(App, j/:table)).joins @orders[n] = App.get_const(App, j/:foreign_table).orders/n @orders["_#{n}"] = App.get_const(App, j/:foreign_table).orders/"_#{n}" #col[:table] = rel_name rescue NoMethodError => e if col.nil? Waxx.debug "col is nil" else Waxx.debug "ERROR: App[#{col[:table]}] has no joins in View.has" end raise e end #Waxx.debug n #Waxx.debug col [n, col] end |