waxx 0.1.4 → 0.2.0

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/lib/waxx/patch.rb CHANGED
@@ -74,6 +74,9 @@ class NilClass
74
74
  def empty?
75
75
  true
76
76
  end
77
+ def any?
78
+ false
79
+ end
77
80
  end
78
81
  class Numeric
79
82
  # HTML format (self -- no escaping needed)
@@ -123,6 +126,10 @@ class String
123
126
  def capitalize_all
124
127
  split(/[ _]/).map{|l| l.capitalize}.join(' ')
125
128
  end
129
+ alias title_case capitalize_all
130
+ def any?
131
+ self != ""
132
+ end
126
133
  end
127
134
  class Time
128
135
  # HTML format
data/lib/waxx/pdf.rb CHANGED
@@ -5,8 +5,8 @@
5
5
 
6
6
  module Waxx::Pdf
7
7
 
8
- def new_doc(margin:50, orientation: "portrait", info: {})
9
- Prawn::Document.new(:margin=>margin, :orientation=>orientation, :info=>info)
8
+ def new_doc(margin: 50, page_size: "LETTER", page_layout: "portrait", info: {})
9
+ Prawn::Document.new(margin: margin, page_size: page_size, page_layout: page_layout, info: info)
10
10
  end
11
11
 
12
12
  def doc_info(
data/lib/waxx/pg.rb CHANGED
@@ -57,10 +57,17 @@ module Waxx::Pg
57
57
  def build_joins(n, col_is)
58
58
  return if col_is.nil?
59
59
  [col_is].flatten.each{|str|
60
- r, tc = str.split(":")
61
- t, c = tc.split(".")
60
+ rt, c = str.split(".")
61
+ r, t = rt.split(":")
62
+ t = r if t.nil?
62
63
  j = c =~ /\+$/ ? "LEFT" : "INNER"
63
- @joins[r] = {table: @table, col: n.to_s.strip, join: j.to_s.strip, foreign_table: t.to_s.strip, foreign_col: c.to_s.strip.sub('+','')}
64
+ @joins[r] = {
65
+ table: @table,
66
+ col: n.to_s.strip,
67
+ join: j.to_s.strip,
68
+ foreign_table: t.to_s.strip,
69
+ foreign_col: c.to_s.strip.sub('+','')
70
+ }
64
71
  }
65
72
  end
66
73
 
@@ -179,11 +186,22 @@ module Waxx::Pg
179
186
  ret = []
180
187
  i = 1
181
188
  cols.each{|n,v|
182
- if data/n
189
+ if data.has_key? n or data.has_key? n.to_s
183
190
  names << n.to_s
184
- vars << "$#{i}"
185
- vals << cast(v, data/n)
186
- i += 1
191
+ # Make empty array array literals
192
+ if v[:type].to_s == 'array' and ((data/n).nil? or (data/n).empty?)
193
+ vars << "'{}'"
194
+ else
195
+ if data/n === :default
196
+ vars << "DEFAULT"
197
+ elsif data/n === :now
198
+ vars << "NOW()"
199
+ else
200
+ vars << "$#{i}"
201
+ vals << cast(v, data/n)
202
+ i += 1
203
+ end
204
+ end
187
205
  end
188
206
  ret << n.to_s
189
207
  }
@@ -192,7 +210,7 @@ module Waxx::Pg
192
210
  sql << " RETURNING #{returning || ret.join(",")}"
193
211
  Waxx.debug(sql)
194
212
  Waxx.debug(vals)
195
- x.db[@db].exec(sql, vals).first
213
+ x.db[@db].exec(sql, vals).first
196
214
  end
197
215
 
198
216
  def put(x, id, data, cols:nil, returning:nil, view:nil, where:nil, &blk)
@@ -205,14 +223,24 @@ module Waxx::Pg
205
223
  sql = "UPDATE #{@table} SET "
206
224
  set = []
207
225
  vals = []
208
- ret = []
226
+ ret = [@pkey]
209
227
  i = 1
210
228
  cols.each{|n,v|
211
229
  if data.has_key? n.to_s or data.has_key? n.to_sym
212
- set << "#{n} = $#{i}"
213
- vals << cast(v, data/n)
230
+ if data/n === :default
231
+ set << "#{n} = DEFAULT"
232
+ elsif data/n === :now
233
+ set << "#{n} = NOW()"
234
+ else
235
+ if !(data/n).nil? and (v/:type).to_s =~ /\[\]$/ and (data/n).empty?
236
+ set << "#{n} = ARRAY[]::#{v/:type}"
237
+ else
238
+ set << "#{n} = $#{i}"
239
+ vals << cast(v, data/n)
240
+ i += 1
241
+ end
242
+ end
214
243
  ret << n.to_s
215
- i += 1
216
244
  end
217
245
  }
218
246
  sql << set.join(",")
@@ -237,6 +265,10 @@ module Waxx::Pg
237
265
  val.to_s.empty? ? nil : val.to_i
238
266
  when :float, :numeric
239
267
  val.to_s.empty? ? nil : val.to_f
268
+ when :bool, :boolean
269
+ val.nil? ? nil : ['t', 'true', 'y', 'yes'].include?(val.to_s.downcase) ? true : false
270
+ when :date, :datetime, :timestamp, :time
271
+ val.to_s.empty? ? nil : val
240
272
  else
241
273
  val
242
274
  end
@@ -252,6 +284,35 @@ module Waxx::Pg
252
284
  @pkey
253
285
  end
254
286
 
287
+ def columns_for(x, table_name, conn_name=:app)
288
+ # Get the primary key
289
+ pkey = x.db[conn_name].exec("
290
+ select column_name
291
+ from information_schema.key_column_usage
292
+ where table_schema = 'public'
293
+ and table_name = $1
294
+ and constraint_name like '%_pkey'",
295
+ [
296
+ table_name
297
+ ]
298
+ ).first['column_name'] rescue nil
299
+ columns = x.db[conn_name].exec("
300
+ select column_name, data_type
301
+ from information_schema.columns
302
+ where table_schema = 'public'
303
+ and table_name = $1
304
+ order by ordinal_position
305
+ ",[
306
+ table_name
307
+ ]
308
+ )
309
+ columns.map{|c|
310
+ c['pkey'] = c['column_name'] == pkey
311
+ c
312
+ }
313
+ end
314
+
315
+
255
316
  def debug(str, level=3)
256
317
  Waxx.debug(str, level)
257
318
  end
data/lib/waxx/res.rb CHANGED
@@ -19,7 +19,7 @@ module Waxx
19
19
  #
20
20
  # ```
21
21
  # x.res[name] = value # Set a response header
22
- # x.res.as(extention) # Set the Content-Type header based on extension
22
+ # x.res.as(extention) # Set the content-type header based on extension
23
23
  # x.res.redirect '/path' # Redirect the client with 302 / Location header
24
24
  # x.res.location '/path' # Redirect the client with 302 / Location header
25
25
  # x.res.cookie( # Set a cookie
@@ -45,7 +45,9 @@ module Waxx
45
45
  :no_cookies
46
46
  ) do
47
47
 
48
- # Send output to the client (may be buffered)
48
+ attr :headers_sent
49
+
50
+ # Send output to the client buffered until flush() or complete()
49
51
  def << str
50
52
  out << str
51
53
  end
@@ -59,7 +61,7 @@ module Waxx
59
61
  end
60
62
 
61
63
  def as(ext)
62
- headers['Content-Type'] = Waxx::Http.ctype(ext)
64
+ headers['content-type'] = Waxx::Http.ctype(ext)
63
65
  end
64
66
 
65
67
  def location(uri)
@@ -79,13 +81,29 @@ module Waxx
79
81
  "\r\n"
80
82
  ].flatten.join("\r\n")
81
83
  end
84
+
85
+ def flush(content=nil)
86
+ unless @headers_sent
87
+ server.print head
88
+ @headers_sent = true
89
+ end
90
+ if content.nil?
91
+ server.print out.join
92
+ out.clear
93
+ else
94
+ server.print content
95
+ end
96
+ end
82
97
 
83
98
  # Output the headers and the body
84
99
  def complete
85
100
  re = out.join
86
- headers["Content-Length"] = re.bytesize
101
+ headers["content-length"] = re.bytesize if headers['content-length'].nil?
87
102
  begin
88
- server.print head
103
+ unless @headers_sent
104
+ server.print head
105
+ @headers_sent = true
106
+ end
89
107
  server.print re
90
108
  # Connection reset by peer
91
109
  rescue Errno::ECONNRESET => e
@@ -100,9 +118,15 @@ module Waxx
100
118
  end
101
119
  end
102
120
 
103
- def cookie(name:"", value:nil, domain:nil, expires:nil, path:"/", secure:true, http_only: false, same_site: "Lax")
104
- expires = expires.nil? ? "" : "expires=#{Time === expires ? expires.rfc2822 : expires}; "
105
- cookies << "#{name}=#{Waxx::Http.escape(value.to_s)}; #{expires}#{";domain=#{domain}" if domain}; path=#{path}#{"; secure" if secure}#{"; HttpOnly" if http_only}; SameSite=#{same_site}"
121
+ def cookie(name: "", value: nil, domain: nil, expires: nil, path: "/", secure: true, http_only: false, same_site: "Lax")
122
+ c = ["#{name}=#{Waxx::Http.escape(value.to_s)}"]
123
+ c << "Path=#{path}"
124
+ c << "SameSite=#{same_site}"
125
+ c << "Expires=#{Time === expires ? expires.rfc2822 : expires}" if expires
126
+ c << "Domain=#{domain}" if domain
127
+ c << "Secure" if secure
128
+ c << "HttpOnly" if http_only
129
+ cookies << c.join("; ")
106
130
  end
107
131
  end
108
132
 
data/lib/waxx/server.rb CHANGED
@@ -44,7 +44,6 @@ module Waxx::Server
44
44
  if not Waxx::Csrf.ok?(x)
45
45
  true
46
46
  end
47
- puts "Cleared CSRF"
48
47
  end
49
48
  false
50
49
  end
@@ -90,8 +89,7 @@ module Waxx::Server
90
89
  #Waxx.debug "no-file"
91
90
  env, head = Waxx::Http.parse_head(io)
92
91
  Waxx.debug [Time.now.to_s, meth, uri].join(" "), 2
93
- #Waxx.debug head, 9
94
- cookie = Waxx::Http.parse_cookie(env['Cookie'])
92
+ cookie = Waxx::Http.parse_cookie(env['cookie'])
95
93
  begin
96
94
  usr = cookie[Waxx['cookie']['user']['name']] ? JSON.parse(::App.decrypt(cookie[Waxx['cookie']['user']['name']][0])) : default_cookies[Waxx['cookie']['user']['name']]
97
95
  rescue => e
@@ -119,6 +117,9 @@ module Waxx::Server
119
117
  res = Waxx::Res.new(io, 200, default_response_headers(req, ext), [], [], [])
120
118
  jobs = []
121
119
  x = Waxx::X.new(req, res, usr, ua, db, meth.downcase.to_sym, app, act, oid, args, ext, jobs).freeze
120
+ if usr['id'].nil? and (get/:qlk or post/:qlk)
121
+ App::Usr.login_by_qlk(x, post/:qlk || get/:qlk)
122
+ end
122
123
  if csrf?(x)
123
124
  Waxx::App.csrf_failure(x)
124
125
  finish(x, io)
@@ -181,7 +182,7 @@ module Waxx::Server
181
182
  def fatal_error(x, e)
182
183
  x.res.status = 503
183
184
  puts "FATAL ERROR: #{e}\n#{e.backtrace}"
184
- report = "APPLICATION ERROR\n=================\n\nUSR:\n\n#{x.usr.map{|n,v| "#{n}: #{v}"}.join("\n") rescue nil}\n\nERROR:\n\n#{e}\n#{e.backtrace.join("\n")}\n\nENV:\n\n#{x.req.env.map{|n,v| "#{n}: #{v}"}.join("\n") rescue nil}\n\nGET:\n\n#{x.req.get.map{|n,v| "#{n}: #{v}"}.join("\n") rescue nil}\n\nPOST:\n\n#{x.req.post.map{|n,v| "#{n}: #{v}"}.join("\n") rescue nil}\n\n"
185
+ report = "APPLICATION ERROR\n=================\n\nUSR:\n\n#{x.usr.map{|n,v| "#{n}: #{v}"}.join("\n") rescue nil}\n\nERROR:\n\n#{e}\n#{e.backtrace.join("\n")}\n\nENV:\n\nHostname: #{`hostname`}\n#{x.req.env.map{|n,v| "#{n}: #{v}"}.join("\n") rescue nil}\n\nGET:\n\n#{x.req.get.map{|n,v| "#{n}: #{v}"}.join("\n") rescue nil}\n\nPOST:\n\n#{x.req.post.map{|n,v| "#{n}: #{v}"}.join("\n") rescue nil}\n\n"
185
186
  if Waxx['debug']['on_screen']
186
187
  x << "<pre>#{report.h}</pre>"
187
188
  else
data/lib/waxx/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Waxx
2
- Version = '0.1.4'
2
+ Version = '0.2.0'
3
3
  end
data/lib/waxx/view.rb CHANGED
@@ -62,6 +62,8 @@ module Waxx::View
62
62
  attr :order_by
63
63
  # A hash of how you can sort this view
64
64
  attr :orders
65
+ # Initial where (view will always include this filter (plus any additional where clauses)
66
+ attr :initial_where
65
67
 
66
68
  ##
67
69
  # Initialize a view. This is normally done automatically when calling `has`.
@@ -78,6 +80,7 @@ module Waxx::View
78
80
  @object = App.get_const(App, @table)
79
81
  @relations = {}
80
82
  @orders = {}
83
+ @initial_where = ["", []]
81
84
  has(*cols) if cols
82
85
  as(layouts) if layouts
83
86
  end
@@ -130,6 +133,8 @@ module Waxx::View
130
133
  Waxx.debug "Column #{c} not defined in #{@object}."
131
134
  #raise "Column #{c} not defined in #{@object}."
132
135
  end
136
+ @orders[n.to_sym] = col/:order || n
137
+ @orders["_#{n}".to_sym] = col/:_order || "#{n} desc"
133
138
  #Waxx.debug @relations.inspect
134
139
  #TODO: Deal with relations that have different names than the tables
135
140
  col[:views] << self rescue col[:views] = [self]
@@ -158,7 +163,13 @@ module Waxx::View
158
163
  col = (App.get_const(App, j/:foreign_table)/col_name).dup
159
164
  col[:table] = rel_name
160
165
  rescue NoMethodError => e
161
- Waxx.debug "ERROR: NoMethodError: #{rel_name} does not define col: #{col_name}"
166
+ if j.nil?
167
+ Waxx.debug %(ERROR: \n\n
168
+ NoMethodError: The #{rel_name} relationship is not defined in any columns of #{@object.name}.
169
+ FIX: Add "is: '#{rel_name}:table.foreign_key'" to a column definition in the 'has' method in #{@object.name}.)
170
+ else
171
+ Waxx.debug "ERROR: NoMethodError: #{rel_name} does not define col: #{col_name}"
172
+ end
162
173
  raise e
163
174
  rescue NameError, TypeError => e
164
175
  Waxx.debug "ERROR: Name or Type Error: #{rel_name} does not define col: #{col_name}"
@@ -259,6 +270,12 @@ module Waxx::View
259
270
  @order_by = ord
260
271
  end
261
272
 
273
+ ##
274
+ # Set the initial where clause. The view will always include this in the where.
275
+ def init_where(sql, args=[])
276
+ @initial_where = [sql, args]
277
+ end
278
+
262
279
  ##
263
280
  # Gets the data for the view and displays it. This is just a shortcut method.
264
281
  #
@@ -301,14 +318,16 @@ module Waxx::View
301
318
  # Automatically build the where clause of SQL based on the parameters passed in and the definition of matches and searches.
302
319
  def build_where(x, args: {}, matches: @matches, searches: @searches)
303
320
  return nil if args.nil? or args.empty? or (matches.nil? and searches.nil?)
304
- w_str = ""
305
- w_args = []
321
+ w_str = @initial_where[0].dup
322
+ w_args = @initial_where[1].dup
306
323
  q = args/:q || x['q']
307
324
  if q and searches
325
+ w_str += " AND " if w_str != ""
308
326
  w_str += "("
309
327
  searches.each_with_index{|c, i|
328
+ col = @columns/c
310
329
  w_str += " OR " if i > 0
311
- w_str += "LOWER(#{c}) like $1"
330
+ w_str += "LOWER(#{col/:table}.#{col/:column}) like $1"
312
331
  }
313
332
  w_args << "%#{q.downcase}%"
314
333
  w_str += ")"
@@ -318,8 +337,16 @@ module Waxx::View
318
337
  next if (x/c).to_s == "" and (args/c).to_s == ""
319
338
  w_str += " AND " if w_str != ""
320
339
  col = self[c.to_sym]
321
- w_str += "#{c} #{col[:match] || "="} $#{w_args.size + 1}"
322
- w_args << (args/c || x/c)
340
+ # match in args needs to be an array
341
+ if col[:match].to_s == 'in'
342
+ w_str += "#{c} in (#{([args/c || x/c].flatten).map{|v|
343
+ w_args << v
344
+ "$#{w_args.size}"
345
+ }.join(',')})"
346
+ else
347
+ w_str += "#{c} #{col[:match] || "="} $#{w_args.size + 1}"
348
+ w_args << (args/c || x/c)
349
+ end
323
350
  }
324
351
  end
325
352
  [w_str, w_args]
@@ -355,8 +382,14 @@ module Waxx::View
355
382
  def put_post(x, id, data, args:nil, returning: nil)
356
383
  @object.put_post(x, id, data, view: self, returning: returning)
357
384
  end
358
- alias post put_post
359
- alias put put_post
385
+
386
+ def post(x, data, returning: nil)
387
+ @object.post(x, data, returning: returning, view: self)
388
+ end
389
+
390
+ def put(x, id, data, returning: nil)
391
+ @object.put(x, id, data, returning: returning, view: self)
392
+ end
360
393
 
361
394
  ##
362
395
  # Delete a record by ID (primary key of the primary object)
data/lib/waxx/waxx.rb CHANGED
@@ -23,15 +23,15 @@ module Waxx
23
23
  # A few helper functions
24
24
 
25
25
  ##
26
- # Shortcut to Waxx::Waxx variables
26
+ # Shortcut to Waxx::Conf variables
27
27
  def [](str)
28
- Waxx::Conf[str]
28
+ Waxx::Conf[str.to_s]
29
29
  end
30
30
 
31
31
  ##
32
- # Shortcut to Waxx::Waxx variables
32
+ # Shortcut to Waxx::Conf variables
33
33
  def /(str)
34
- Waxx::Conf/str
34
+ Waxx::Conf[str.to_s]
35
35
  end
36
36
 
37
37
  ##
data/lib/waxx/x.rb CHANGED
@@ -76,10 +76,22 @@ module Waxx
76
76
  res << str.to_s
77
77
  end
78
78
  def [](k)
79
- req.post[k.to_s] || req.get[k.to_s]
79
+ begin
80
+ req.post.has_key?(k) ? req.post[k] :
81
+ req.post.has_key?(k.to_s) ? req.post[k.to_s] :
82
+ req.get/k
83
+ rescue
84
+ nil
85
+ end
80
86
  end
81
87
  def /(k)
82
- req.post[k.to_s] || req.get[k.to_s]
88
+ begin
89
+ req.post.has_key?(k) ? req.post[k] :
90
+ req.post.has_key?(k.to_s) ? req.post[k.to_s] :
91
+ req.get/k
92
+ rescue
93
+ nil
94
+ end
83
95
  end
84
96
  def usr?
85
97
  not (usr['id'].nil? or usr['id'].to_i == 0)
data/lib/waxx.rb CHANGED
@@ -14,7 +14,6 @@
14
14
 
15
15
  # Libs (core & std)
16
16
  require 'socket'
17
- require 'thread'
18
17
  require 'openssl'
19
18
  require 'base64'
20
19
  require 'json'
@@ -35,6 +34,7 @@ require_relative 'waxx/csrf'
35
34
  require_relative 'waxx/database'
36
35
  require_relative 'waxx/encrypt'
37
36
  require_relative 'waxx/error'
37
+ require_relative 'waxx/generate'
38
38
  require_relative 'waxx/html'
39
39
  require_relative 'waxx/http'
40
40
  require_relative 'waxx/json'
@@ -0,0 +1,32 @@
1
+ module App::About
2
+ extend Waxx::Object
3
+ extend self
4
+
5
+ runs(
6
+ index: {
7
+ desc: "The about main page.",
8
+ get: -> (x) {
9
+ App::Html.page(x, title: "About", content: %(
10
+ This is the main about page.
11
+ ))
12
+ }
13
+ },
14
+ privacy: {
15
+ desc: "The privacy policy",
16
+ get: -> (x) {
17
+ App::Html.page(x, title: "Privacy Policy", content: %(
18
+ You can pull this content from the file system or the database or just leave it in the code.
19
+ ))
20
+ }
21
+ },
22
+ terms: {
23
+ desc: "The terms of use",
24
+ get: -> (x) {
25
+ App::Html.page(x, title: "Terms of Use", content: %(
26
+ You can pull this content from the file system or the database or just leave it in the code.
27
+ ))
28
+ }
29
+ },
30
+ )
31
+
32
+ end
@@ -4,142 +4,12 @@ module App::Home::Html
4
4
 
5
5
  def welcome(x)
6
6
  App::Html.render(x, title: "Welcome to Waxx", content: %(
7
- #{carousel(x)}
8
- <div class="container marketing">
9
- #{main_points(x)}
10
- #{featurettes(x)}
11
- </div>
7
+ <h1>Waxx</h1>
8
+ <p>
9
+ This is the default home page of your Waxx app.
10
+ The file is <code>app/home/html.rb</code>
11
+ </p>
12
12
  ))
13
13
  end
14
-
15
14
 
16
- def carousel(x)
17
- %(
18
- <!-- Carousel
19
- ================================================== -->
20
- <div id="myCarousel" class="carousel slide" data-ride="carousel">
21
- <!-- Indicators -->
22
- <ol class="carousel-indicators">
23
- <li data-target="#myCarousel" data-slide-to="0" class="active"></li>
24
- <li data-target="#myCarousel" data-slide-to="1"></li>
25
- <li data-target="#myCarousel" data-slide-to="2"></li>
26
- </ol>
27
- <div class="carousel-inner" role="listbox">
28
- <div class="item active">
29
- <img class="first-slide" src="data:image/gif;base64,R0lGODlhAQABAIAAAHd3dwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==" alt="First slide">
30
- <div class="container">
31
- <div class="carousel-caption">
32
- <h1>Fast and Smooth</h1>
33
- <p>Waxx makes web development fast and smooth. You can edit this page at <br><code>app/home/html.rb</code></p>
34
- <p><a class="btn btn-lg btn-primary" href="#" role="button">Sign up today</a></p>
35
- </div>
36
- </div>
37
- </div>
38
- <div class="item">
39
- <img class="second-slide" src="data:image/gif;base64,R0lGODlhAQABAIAAAHd3dwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==" alt="Second slide">
40
- <div class="container">
41
- <div class="carousel-caption">
42
- <h1>Another example headline.</h1>
43
- <p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.</p>
44
- <p><a class="btn btn-lg btn-primary" href="#" role="button">Learn more</a></p>
45
- </div>
46
- </div>
47
- </div>
48
- <div class="item">
49
- <img class="third-slide" src="data:image/gif;base64,R0lGODlhAQABAIAAAHd3dwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==" alt="Third slide">
50
- <div class="container">
51
- <div class="carousel-caption">
52
- <h1>One more for good measure.</h1>
53
- <p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.</p>
54
- <p><a class="btn btn-lg btn-primary" href="#" role="button">Browse gallery</a></p>
55
- </div>
56
- </div>
57
- </div>
58
- </div>
59
- <a class="left carousel-control" href="#myCarousel" role="button" data-slide="prev">
60
- <span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span>
61
- <span class="sr-only">Previous</span>
62
- </a>
63
- <a class="right carousel-control" href="#myCarousel" role="button" data-slide="next">
64
- <span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span>
65
- <span class="sr-only">Next</span>
66
- </a>
67
- </div><!-- /.carousel -->
68
- )
69
- end
70
-
71
- def main_points(x)
72
- %(
73
- <!-- Main Points
74
- ================================================== -->
75
-
76
- <!-- Three columns of text below the carousel -->
77
- <div class="row">
78
- <div class="col-lg-4">
79
- <img class="img-circle" src="data:image/gif;base64,R0lGODlhAQABAIAAAHd3dwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==" alt="Generic placeholder image" width="140" height="140">
80
- <h2>Heading</h2>
81
- <p>Donec sed odio dui. Etiam porta sem malesuada magna mollis euismod. Nullam id dolor id nibh ultricies vehicula ut id elit. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Praesent commodo cursus magna.</p>
82
- <p><a class="btn btn-default" href="#" role="button">View details &raquo;</a></p>
83
- </div><!-- /.col-lg-4 -->
84
- <div class="col-lg-4">
85
- <img class="img-circle" src="data:image/gif;base64,R0lGODlhAQABAIAAAHd3dwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==" alt="Generic placeholder image" width="140" height="140">
86
- <h2>Heading</h2>
87
- <p>Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Cras mattis consectetur purus sit amet fermentum. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh.</p>
88
- <p><a class="btn btn-default" href="#" role="button">View details &raquo;</a></p>
89
- </div><!-- /.col-lg-4 -->
90
- <div class="col-lg-4">
91
- <img class="img-circle" src="data:image/gif;base64,R0lGODlhAQABAIAAAHd3dwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==" alt="Generic placeholder image" width="140" height="140">
92
- <h2>Heading</h2>
93
- <p>Donec sed odio dui. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Vestibulum id ligula porta felis euismod semper. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.</p>
94
- <p><a class="btn btn-default" href="#" role="button">View details &raquo;</a></p>
95
- </div><!-- /.col-lg-4 -->
96
- </div><!-- /.row -->
97
- )
98
- end
99
-
100
- def featurettes(x)
101
- %(
102
- <!-- START THE FEATURETTES -->
103
-
104
- <hr class="featurette-divider">
105
-
106
- <div class="row featurette">
107
- <div class="col-md-7">
108
- <h2 class="featurette-heading">First featurette heading. <span class="text-muted">It'll blow your mind.</span></h2>
109
- <p class="lead">Donec ullamcorper nulla non metus auctor fringilla. Vestibulum id ligula porta felis euismod semper. Praesent commodo cursus magna, vel scelerisque nisl consectetur. Fusce dapibus, tellus ac cursus commodo.</p>
110
- </div>
111
- <div class="col-md-5">
112
- <img class="featurette-image img-responsive center-block" data-src="holder.js/500x500/auto" alt="Generic placeholder image">
113
- </div>
114
- </div>
115
-
116
- <hr class="featurette-divider">
117
-
118
- <div class="row featurette">
119
- <div class="col-md-7 col-md-push-5">
120
- <h2 class="featurette-heading">Oh yeah, it's that good. <span class="text-muted">See for yourself.</span></h2>
121
- <p class="lead">Donec ullamcorper nulla non metus auctor fringilla. Vestibulum id ligula porta felis euismod semper. Praesent commodo cursus magna, vel scelerisque nisl consectetur. Fusce dapibus, tellus ac cursus commodo.</p>
122
- </div>
123
- <div class="col-md-5 col-md-pull-7">
124
- <img class="featurette-image img-responsive center-block" data-src="holder.js/500x500/auto" alt="Generic placeholder image">
125
- </div>
126
- </div>
127
-
128
- <hr class="featurette-divider">
129
-
130
- <div class="row featurette">
131
- <div class="col-md-7">
132
- <h2 class="featurette-heading">And lastly, this one. <span class="text-muted">Checkmate.</span></h2>
133
- <p class="lead">Donec ullamcorper nulla non metus auctor fringilla. Vestibulum id ligula porta felis euismod semper. Praesent commodo cursus magna, vel scelerisque nisl consectetur. Fusce dapibus, tellus ac cursus commodo.</p>
134
- </div>
135
- <div class="col-md-5">
136
- <img class="featurette-image img-responsive center-block" data-src="holder.js/500x500/auto" alt="Generic placeholder image">
137
- </div>
138
- </div>
139
-
140
- <hr class="featurette-divider">
141
-
142
- <!-- /END THE FEATURETTES -->
143
- )
144
- end
145
15
  end