require 'snails' unless defined?(Snails) %w( snails/util active_support/core_ext/hash sinatra/base sinatra/content_for sinatra/flash ).each { |lib| require lib } module Snails module All # usage: # # class App < Snails::App # register Snails::All # end def self.registered(app) app.register Snails::Database app.register Snails::Locales app.register Snails::Assets app.register Snails::Sessions end end module Database def self.registered(app) require 'sinatra/activerecord' app.register Sinatra::ActiveRecordExtension # app.configure :development do # ActiveRecord::Base.logger.level = Logger::DEBUG # end end end module Locales def self.load!(path, lang = nil) I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks) I18n.load_path = Dir[File.join(path, '*.yml')] I18n.enforce_available_locales = false I18n.backend.load_translations I18n.locale = lang if lang end def self.registered(app) require 'i18n' require 'i18n/backend/fallbacks' cwd = Pathname.new(Dir.pwd) app.set :locale, :es app.set :locales_path, app.setting(:locales_path, cwd.join('config', 'locales')) app.helpers do def t(key); I18n.t(key); end end app.configure do Locales.load!(app.settings.locales_path) end app.before do I18n.locale = app.settings.locale end end end # usage: # class App < Snails::App # register Snails::Assets # # # optional: # set :assets_precompile, %w(js/app.js css/styles.css) # # also optional, set compressor # sprockets.css_compressor = :csso # sprockets.js_compressor = :uglifier # end # Then, in your view: # # # # module Assets def self.registered(app) require 'sprockets-helpers' cwd = Pathname.new(Dir.pwd) app.set :sprockets, Sprockets::Environment.new(cwd) app.set :digest_assets, false app.set :assets_prefix, app.setting(:assets_prefix, '/assets') # URL app.set :assets_paths, app.setting(:assets_paths, %w(assets)) # source files app.set :assets_public_path, app.setting(:assets_public_path, cwd.join('public', 'assets')) # output dir app.set :assets_precompile, app.setting(:assets_precompile, %w(js/main.js css/main.css)) app.set :assets_remove_digests, app.setting(:assets_remove_digests, false) app.configure do app.assets_paths.each do |path| app.sprockets.append_path cwd.join(path) end end app.configure :production, :staging do # app.sprockets.css_compressor = :sass # app.sprockets.js_compressor = :uglifier end app.configure :development do # allow asset requests to pass if app.respond_to?(:allow_paths) app.allow_paths.push /^#{app.assets_prefix}(\/\w+)?\/([\w\.-]+)/ end # and serve them app.get "#{app.assets_prefix}/*" do |path| env_sprockets = request.env.dup env_sprockets['PATH_INFO'] = path app.sprockets.call(env_sprockets) end end app.helpers do def asset_path(filename) file = manifest[filename] or raise "Not found in manifest: #{filename}" [settings.assets_prefix, file].join('/') end if Snails.env.production? def manifest @manifest ||= read_manifest end else def manifest read_manifest end end def read_manifest file = Dir[settings.assets_public_path + '/.*.json'].first or raise "No manifest found at #{path}" JSON.parse(IO.read(file))['assets'] end end end module Tasks def self.precompile_for(app) unless app.respond_to?(:assets_public_path) return puts "#{app.name} doesn't have the Asset module included." end puts "Precompiling #{app.name} assets to #{app.assets_public_path}..." FileUtils.remove_dir(app.assets_public_path.to_s, true) environment = app.sprockets manifest = ::Sprockets::Manifest.new(environment.index, app.assets_public_path) manifest.compile(app.assets_precompile) if app.assets_remove_digests? # files = Dir[app.assets_public_path.to_s + '/*/*'] files = `find #{app.assets_public_path}`.split("\n").select { |f| f[/\.(js|css)/] } remove_digests(files) end end private def self.remove_digests(files) puts "Removing digests from #{files.length} files..." files.each do |file| dir = File.dirname(file) parts = File.basename(file).split(/-|\./) if !parts[1] or parts[1].length < 10 # puts "This doesn't look like a digested file: #{file}. Skipping..." next end dest = File.join(dir, "#{parts.first}.#{parts.last}").sub('.gz', '.' + parts.last(2).join('.')) FileUtils.mv(file, dest) puts " --> #{dest}" end end end end module RequiredParams def requires!(req, hash = params) if req.is_a?(Hash) req.each do |k, vals| if vals.is_a?(Array) or vals.is_a?(Hash) halt(400, "Missing: #{k} in #{hash}") if hash[k].nil? requires!(vals, hash[k]) else requires!(k, hash) end end elsif req.nil? or (req.is_a?(Symbol) and hash[req].nil?) \ or (req.is_a?(Array) and req.any? { |p| hash[p].nil? }) halt(400, "Required parameters: #{req} (in #{hash})") end end end module FormHelpers def form_input(object, field, options = {}) id, name, index, label = input_base(object, field, options) type = (options[:type] || :text).to_sym value = options[:value] ? "value=\"#{options[:value]}\"" : (type == :password ? '' : "value=\"#{object.send(field)}\"") classes = object.errors[field].any? ? 'has-errors' : '' label + raw_input(index, type, id, name, value, options[:placeholder], classes, options[:required]) end def form_password(object, field, options = {}) form_input(object, field, {:type => 'password'}.merge(options)) end def form_checkbox(object, field, options = {}) id, name, index, label = input_base(object, field, options) type = options[:type] || :checkbox value = options[:value] ? "value='#{options[:value]}'" : '' if type.to_sym == :radio checked = object.send(field) == options[:value] value += checked ? " selected='selected'" : '' else checked = object.send(field) value += checked ? " checked='true'" : '' end label + raw_input(index, type, id, name, options[:placeholder], value) end def form_textarea(object, field, options = {}) id, name, index, label = input_base(object, field, options) style = options[:style] ? "style='#{options[:style]}'" : '' label + "" end def form_select(object, field, option_list, options = {}) id, name, index, label = input_base(object, field, options) style = options[:style] ? "style='#{options[:style]}'" : '' options = form_select_options(option_list, object.send(field)) label + "" end def form_select_options(list, selected = nil) list.map do |name, val| val = name if val.nil? sel = val == selected ? 'selected="selected"' : '' "" end.join("\n") end def form_put "" end def form_submit(text = 'Actualizar', classes = '') @tabindex = @tabindex ? @tabindex + 1 : 1 "" end def post_button(text, path, opts = {}) form_id = opts.delete(:form_id) || path.gsub(/[^a-z0-9]/, '_').gsub(/_+/, '_') css_class = opts.delete(:css_class) || '' input_html = opts.delete(:input_html) || '' submit_val = opts.delete(:value) || text style = "style='display:inline; #{opts[:style]}'" onsubmit = if str = opts[:onsubmit] "onsubmit=\"#{str}\"" else confirm_text = opts.delete(:confirm_text) || 'Seguro?' "onsubmit='return confirm(\"#{confirm_text}\");'" end form_tag = "
' end def delete_link(options = {}) post_button(options[:text], options[:path], options.merge({ input_html: "", css_class: options[:css_class] || 'danger' })) end protected def input_base(object, field, options) label = options[:label] || field.to_s.gsub('_', ' ').capitalize example = options[:example] ? "#{options[:example]}" : '' id = "#{get_model_name(object).downcase}_#{options[:key] || field}" name = "#{get_model_name(object).downcase}[#{field}]" label = options[:label] == false ? '' : "\n" @tabindex = @tabindex ? @tabindex + 1 : 1 return id, name, @tabindex, label end def raw_input(index, type, id, name, value, placeholder = '', classes = '', required = false) req = required ? "required" : '' "" end def get_model_name(obj) obj.respond_to?(:field_name) ? obj.field_name : get_class_name(obj.class) end def get_class_name(klass) klass.model_name.to_s.split("::").last end end module ViewHelpers def self.included(base) Time.include(RelativeTime) unless Time.instance_methods.include?(:relative) base.include(FormHelpers) base.include(SimpleFormat) end def action request.path_info.gsub('/','').blank? ? 'home' : request.path_info.gsub('/',' ') end def partial(name, opts = {}) partial_name = name.to_s["/"] ? name.to_s.reverse.sub("/", "_/").reverse : "_#{name}" erb(partial_name.to_sym, { layout: false }.merge(opts)) end def view(view_name, opts = {}) layout = request.xhr? ? false : true erb(view_name.to_sym, { layout: layout }.merge(opts)) end ######################################### # pagination def get_page(counter) curr = params[:page].to_i i = (curr == 0 && counter == 1) ? 2 : (curr == 2 && counter == -1) ? 0 : curr + counter i == 0 ? "" : "/page/#{i}" end def show_pager(array, path, per_page = 50) # remove page from path path = (env['SCRIPT_NAME'] + path.gsub(/[?|&|\/]page[=|\/]\d+/,'')) prevlink = "