bowtie 0.3.2 → 0.4
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/README.md +68 -0
- data/bowtie.gemspec +33 -15
- data/lib/bowtie.rb +15 -7
- data/lib/bowtie/adapters/datamapper.rb +126 -0
- data/lib/bowtie/adapters/mongomapper.rb +122 -0
- data/lib/bowtie/admin.rb +24 -26
- data/lib/bowtie/core_extensions.rb +24 -28
- data/lib/bowtie/helpers.rb +23 -3
- data/lib/bowtie/public/css/bowtie.css +50 -9
- data/lib/bowtie/public/js/bowtie.js +1 -1
- data/lib/bowtie/views/errors.erb +1 -1
- data/lib/bowtie/views/field.erb +3 -3
- data/lib/bowtie/views/form.erb +12 -11
- data/lib/bowtie/views/index.erb +3 -3
- data/lib/bowtie/views/row.erb +15 -16
- data/lib/bowtie/views/search.erb +2 -2
- data/lib/bowtie/views/show.erb +1 -1
- data/lib/bowtie/views/table.erb +17 -14
- metadata +8 -55
- data/README.rdoc +0 -48
    
        data/README.md
    ADDED
    
    | @@ -0,0 +1,68 @@ | |
| 1 | 
            +
            ## Bowtie: Zeroconf admin scaffold for your MongoMapper & DataMapper models
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            Bowtie reads the information on your models and creates a nice panel in which you can view, edit and destroy records easily.
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            ## How does it look?
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            ## Installation
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            Include it in your Gemfile and update your bundle:
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                source 'rubygems.org'
         | 
| 14 | 
            +
                ..
         | 
| 15 | 
            +
                gem 'bowtie'
         | 
| 16 | 
            +
              
         | 
| 17 | 
            +
            Or install it by hand:
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                $ gem install bowtie
         | 
| 20 | 
            +
             | 
| 21 | 
            +
            ## Configuration
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            Mount Bowtie wherever you want by editing your config.ru file, after loading your models. You can optionally include the admin/pass combination for the panel.
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                require 'my_app' # models are loaded
         | 
| 26 | 
            +
                require 'bowtie'
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                BOWTIE_AUTH = {:user => 'admin', :pass => '12345' }
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                app = Rack::Builder.new {
         | 
| 31 | 
            +
                  map "/admin" do
         | 
| 32 | 
            +
                    run Bowtie::Admin
         | 
| 33 | 
            +
                  end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                  map '/' do
         | 
| 36 | 
            +
                    run MyApp
         | 
| 37 | 
            +
                  end
         | 
| 38 | 
            +
                }
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                run app
         | 
| 41 | 
            +
             | 
| 42 | 
            +
            Now you can go to /admin in your app's path and try out Bowtie using your user/pass combination. If not set, it defaults to admin/bowtie.
         | 
| 43 | 
            +
             | 
| 44 | 
            +
            ## Important notes
         | 
| 45 | 
            +
             | 
| 46 | 
            +
            Bowtie requires a few gems but they're not included in the gemspec to prevent forcing your from installing unneeded gems. Therefore you need to make sure that Bowtie will have the following gems to work with: 
         | 
| 47 | 
            +
             | 
| 48 | 
            +
            For DataMapper models: 
         | 
| 49 | 
            +
             | 
| 50 | 
            +
             * dm-core
         | 
| 51 | 
            +
             * dm-validations
         | 
| 52 | 
            +
             * dm-aggregates
         | 
| 53 | 
            +
             * dm-pager
         | 
| 54 | 
            +
             | 
| 55 | 
            +
            For MongoMapper models:
         | 
| 56 | 
            +
             
         | 
| 57 | 
            +
             * mongo_mapper
         | 
| 58 | 
            +
             | 
| 59 | 
            +
            From version 0.3, Bowtie is meant to be used from DataMapper 1.0.0 on. For previous versions please install with -v=0.2.5.
         | 
| 60 | 
            +
             | 
| 61 | 
            +
            ## TODO
         | 
| 62 | 
            +
             | 
| 63 | 
            +
             * Better handling of types (Text, JSON, IPAddress) in #show
         | 
| 64 | 
            +
             * Better handling of relationships in #show
         | 
| 65 | 
            +
             | 
| 66 | 
            +
            ## Copyright
         | 
| 67 | 
            +
             | 
| 68 | 
            +
            (c) 2010-2011 - Tomás Pollak for Fork Ltd. Released under the MIT license.
         | 
    
        data/bowtie.gemspec
    CHANGED
    
    | @@ -1,21 +1,48 @@ | |
| 1 | 
            -
             | 
| 2 1 | 
             
            Gem::Specification.new do |s|
         | 
| 3 2 | 
             
              s.name = %q{bowtie}
         | 
| 4 | 
            -
              s.version = "0. | 
| 3 | 
            +
              s.version = "0.4"
         | 
| 5 4 |  | 
| 6 5 | 
             
              s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
         | 
| 7 6 | 
             
              s.authors = ["Tomás Pollak"]
         | 
| 8 7 | 
             
              s.date = %q{2010-06-06}
         | 
| 9 | 
            -
              s.description = %q{ | 
| 8 | 
            +
              s.description = %q{Simple admin scaffold for MongoMapper and DataMapper models.}
         | 
| 10 9 | 
             
              s.email = %q{tomas@forkhq.com}
         | 
| 11 | 
            -
              s.extra_rdoc_files = [ | 
| 12 | 
            -
             | 
| 10 | 
            +
              s.extra_rdoc_files = [ "lib/bowtie.rb", 
         | 
| 11 | 
            +
                                     "lib/bowtie/admin.rb", 
         | 
| 12 | 
            +
                                     "lib/bowtie/core_extensions.rb", 
         | 
| 13 | 
            +
                                     "lib/bowtie/helpers.rb" ]
         | 
| 14 | 
            +
             | 
| 15 | 
            +
              s.files = [ "README.md", 
         | 
| 16 | 
            +
                          "bowtie.gemspec", 
         | 
| 17 | 
            +
                          "lib/bowtie.rb", 
         | 
| 18 | 
            +
                          "lib/bowtie/adapters/datamapper.rb", 
         | 
| 19 | 
            +
                          "lib/bowtie/adapters/mongomapper.rb", 
         | 
| 20 | 
            +
                          "lib/bowtie/admin.rb", 
         | 
| 21 | 
            +
                          "lib/bowtie/core_extensions.rb", 
         | 
| 22 | 
            +
                          "lib/bowtie/helpers.rb", 
         | 
| 23 | 
            +
                          "lib/bowtie/views/errors.erb", 
         | 
| 24 | 
            +
                          "lib/bowtie/views/field.erb", 
         | 
| 25 | 
            +
                          "lib/bowtie/views/flash.erb", 
         | 
| 26 | 
            +
                          "lib/bowtie/views/form.erb", 
         | 
| 27 | 
            +
                          "lib/bowtie/views/index.erb", 
         | 
| 28 | 
            +
                          "lib/bowtie/views/layout.erb", 
         | 
| 29 | 
            +
                          "lib/bowtie/views/new.erb", 
         | 
| 30 | 
            +
                          "lib/bowtie/views/search.erb", 
         | 
| 31 | 
            +
                          "lib/bowtie/views/show.erb", 
         | 
| 32 | 
            +
                          "lib/bowtie/views/subtypes.erb",
         | 
| 33 | 
            +
                          "lib/bowtie/views/table.erb", 
         | 
| 34 | 
            +
                          "lib/bowtie/views/row.erb", 
         | 
| 35 | 
            +
                          "lib/bowtie/public/css/bowtie.css", 
         | 
| 36 | 
            +
                          "lib/bowtie/public/js/bowtie.js", 
         | 
| 37 | 
            +
                          "lib/bowtie/public/js/jquery.tablesorter.pack.js", 
         | 
| 38 | 
            +
                          "lib/bowtie/public/js/jquery.jeditable.pack.js" ]
         | 
| 39 | 
            +
             | 
| 13 40 | 
             
              s.homepage = %q{http://github.com/tomas/bowtie}
         | 
| 14 41 | 
             
              s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Bowtie", "--main", "README"]
         | 
| 15 42 | 
             
              s.require_paths = ["lib"]
         | 
| 16 43 | 
             
              s.rubyforge_project = %q{bowtie}
         | 
| 17 44 | 
             
              s.rubygems_version = %q{1.3.5}
         | 
| 18 | 
            -
              s.summary = %q{Bowtie Admin}
         | 
| 45 | 
            +
              s.summary = %q{Bowtie Admin Scaffold}
         | 
| 19 46 |  | 
| 20 47 | 
             
              if s.respond_to? :specification_version then
         | 
| 21 48 | 
             
                current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
         | 
| @@ -23,19 +50,10 @@ Gem::Specification.new do |s| | |
| 23 50 |  | 
| 24 51 | 
             
                if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
         | 
| 25 52 | 
             
                  s.add_runtime_dependency(%q<sinatra>, [">= 0.9.4"])
         | 
| 26 | 
            -
                  s.add_runtime_dependency(%q<dm-core>, [">= 0.10.2"])
         | 
| 27 | 
            -
                  s.add_runtime_dependency(%q<dm-aggregates>, [">= 0.10.2"])
         | 
| 28 | 
            -
                  s.add_runtime_dependency(%q<dm-pager>, [">= 1.0.1"])
         | 
| 29 53 | 
             
                else
         | 
| 30 54 | 
             
                  s.add_dependency(%q<sinatra>, [">= 0.9.4"])
         | 
| 31 | 
            -
                  s.add_dependency(%q<dm-core>, [">= 0.10.2"])
         | 
| 32 | 
            -
                  s.add_dependency(%q<dm-aggregates>, [">= 0.10.2"])
         | 
| 33 | 
            -
                  s.add_dependency(%q<dm-pager>, [">= 1.0.1"])
         | 
| 34 55 | 
             
                end
         | 
| 35 56 | 
             
              else
         | 
| 36 57 | 
             
                s.add_dependency(%q<sinatra>, [">= 0.9.4"])
         | 
| 37 | 
            -
                s.add_dependency(%q<dm-core>, [">= 0.10.2"])
         | 
| 38 | 
            -
                s.add_dependency(%q<dm-aggregates>, [">= 0.10.2"])
         | 
| 39 | 
            -
                s.add_dependency(%q<dm-pager>, [">= 1.0.1"])
         | 
| 40 58 | 
             
              end
         | 
| 41 59 | 
             
            end
         | 
    
        data/lib/bowtie.rb
    CHANGED
    
    | @@ -1,10 +1,18 @@ | |
| 1 | 
            -
            libdir = File.dirname(__FILE__)
         | 
| 1 | 
            +
            libdir = File.expand_path(File.dirname(__FILE__)) + '/bowtie'
         | 
| 2 2 | 
             
            $LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
         | 
| 3 3 |  | 
| 4 | 
            -
             | 
| 5 | 
            -
             | 
| 6 | 
            -
             | 
| 7 | 
            -
            	 | 
| 8 | 
            -
             | 
| 9 | 
            -
            	 | 
| 4 | 
            +
            if defined?(DataMapper)
         | 
| 5 | 
            +
            	begin
         | 
| 6 | 
            +
            		%w(dm-core dm-validations dm-aggregates dm-pager json adapters/datamapper).each {|lib| require lib}
         | 
| 7 | 
            +
            	rescue Gem::LoadError => e
         | 
| 8 | 
            +
            		raise Gem::LoadError, "Seems you have an outdated version of one of the following gems: dm-core dm-validations dm-aggregates dm-pager"
         | 
| 9 | 
            +
            	rescue LoadError => e
         | 
| 10 | 
            +
            		raise Gem::LoadError, "Error loading DataMapper gems. Make sure the all the following gems are available: dm-core dm-validations dm-aggregates dm-pager"
         | 
| 11 | 
            +
            	end
         | 
| 12 | 
            +
            elsif defined?(MongoMapper)
         | 
| 13 | 
            +
            	%w(mongo_mapper adapters/mongomapper).each {|lib| require lib}
         | 
| 14 | 
            +
            else
         | 
| 15 | 
            +
            	raise Gem::LoadError, "No adapters found. You need to require MongoMapper (mongo_mapper) or DataMapper (dm-core) before requiring Bowtie."
         | 
| 10 16 | 
             
            end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
            %w(sinatra helpers core_extensions admin).each {|lib| require lib}
         | 
| @@ -0,0 +1,126 @@ | |
| 1 | 
            +
            module Bowtie
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            	def self.models
         | 
| 4 | 
            +
            		DataMapper::Model.descendants.to_a
         | 
| 5 | 
            +
            	end
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            	def self.search(params, page)
         | 
| 8 | 
            +
            		query1, query2 = [], []
         | 
| 9 | 
            +
            		params.each do |key, val|
         | 
| 10 | 
            +
            			query1 << "#{model}.all(:#{key} => '#{val}')"
         | 
| 11 | 
            +
            		end
         | 
| 12 | 
            +
            		model.searchable_fields.each do |field|
         | 
| 13 | 
            +
            			query2 << "#{model}.all(:#{field}.like => '%#{params[:q]}%')"
         | 
| 14 | 
            +
            		end
         | 
| 15 | 
            +
            		query = query1.any? ? [query1.join(' & '), query2.join(' + ')].join(' & ') : query2.join(' + ')
         | 
| 16 | 
            +
            		@resources = eval(query).page(page, :per_page => PER_PAGE)
         | 
| 17 | 
            +
            		@subtypes = model.subtypes
         | 
| 18 | 
            +
            	end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
            	def self.get_many(model, params, page)
         | 
| 21 | 
            +
            		add_paging(model.all(params), page)
         | 
| 22 | 
            +
            	end
         | 
| 23 | 
            +
            	
         | 
| 24 | 
            +
            	def self.get_one(model, id)
         | 
| 25 | 
            +
            		model.get(id) 
         | 
| 26 | 
            +
            	end
         | 
| 27 | 
            +
            	
         | 
| 28 | 
            +
            	def self.create(model, params)
         | 
| 29 | 
            +
            		model.create(params)
         | 
| 30 | 
            +
            	end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
            	def self.get_associated(model, params)
         | 
| 33 | 
            +
            		res = model.get(params[:id]).send(params[:association])
         | 
| 34 | 
            +
            		# if model.associations[params[:association]].type == :many
         | 
| 35 | 
            +
            		# 	add_paging(res, params)
         | 
| 36 | 
            +
            		# end
         | 
| 37 | 
            +
            	end
         | 
| 38 | 
            +
            	
         | 
| 39 | 
            +
            	# doesnt trigger validations or callbacks
         | 
| 40 | 
            +
            	def self.update!(resource, params)
         | 
| 41 | 
            +
            		resource.update!(params)
         | 
| 42 | 
            +
            	end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
            	def self.update(resource, params)
         | 
| 45 | 
            +
            		resource.update(params)
         | 
| 46 | 
            +
            	end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
            	def self.add_paging(resources, page)
         | 
| 49 | 
            +
            		resources.respond_to?(:page) ? resources.page(page, :per_page => PER_PAGE) : resources
         | 
| 50 | 
            +
            	end
         | 
| 51 | 
            +
            	
         | 
| 52 | 
            +
            	def self.belongs_to_association?(assoc)
         | 
| 53 | 
            +
            		assoc.class == DataMapper::Associations::ManyToOne::Relationship
         | 
| 54 | 
            +
            	end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
            	def self.has_one_association?(assoc)
         | 
| 57 | 
            +
            		assoc.class == DataMapper::Associations::OneToOne::Relationship
         | 
| 58 | 
            +
            	end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
            	module Helpers
         | 
| 61 | 
            +
             | 
| 62 | 
            +
            		def total_entries(resources)
         | 
| 63 | 
            +
            			resources.respond_to?(:pager) ? resources.pager.total : resources.count
         | 
| 64 | 
            +
            		end
         | 
| 65 | 
            +
            		
         | 
| 66 | 
            +
            		def show_pager(resources, path)
         | 
| 67 | 
            +
            			resources.pager.to_html(base_path + path) if resources.respond_to?(:pager)
         | 
| 68 | 
            +
            		end
         | 
| 69 | 
            +
            		
         | 
| 70 | 
            +
            	end
         | 
| 71 | 
            +
             | 
| 72 | 
            +
            end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
            class Object
         | 
| 75 | 
            +
            	def primary_key
         | 
| 76 | 
            +
            		send(self.class.primary_key)
         | 
| 77 | 
            +
            	end
         | 
| 78 | 
            +
             | 
| 79 | 
            +
            	def to_json
         | 
| 80 | 
            +
            		attributes.to_json
         | 
| 81 | 
            +
            	end
         | 
| 82 | 
            +
             | 
| 83 | 
            +
            end
         | 
| 84 | 
            +
             | 
| 85 | 
            +
            class Class
         | 
| 86 | 
            +
             | 
| 87 | 
            +
            	def primary_key
         | 
| 88 | 
            +
            		key.first.name
         | 
| 89 | 
            +
            	end
         | 
| 90 | 
            +
             | 
| 91 | 
            +
            	def model_associations
         | 
| 92 | 
            +
            		h = {}
         | 
| 93 | 
            +
            		relationships.map {|r| h[r.name] = r }
         | 
| 94 | 
            +
            		h
         | 
| 95 | 
            +
            	end
         | 
| 96 | 
            +
             | 
| 97 | 
            +
            	def field_names
         | 
| 98 | 
            +
            		self.properties.collect{|p| p.name }
         | 
| 99 | 
            +
            	end
         | 
| 100 | 
            +
             | 
| 101 | 
            +
            	def boolean_fields
         | 
| 102 | 
            +
            		self.properties.map{|a| a.name if a.class == DataMapper::Property::Boolean}.compact
         | 
| 103 | 
            +
            	end
         | 
| 104 | 
            +
             | 
| 105 | 
            +
            	def searchable_fields
         | 
| 106 | 
            +
            		self.properties.map{|a| a.name if a.class == DataMapper::Property::String}.compact
         | 
| 107 | 
            +
            	end
         | 
| 108 | 
            +
             | 
| 109 | 
            +
            	def subtypes
         | 
| 110 | 
            +
            		begin
         | 
| 111 | 
            +
            			self.validators.first.last.map{|a,b| b = {a.field_name => a.options[:set]} if a.class == DataMapper::Validate::WithinValidator}.compact
         | 
| 112 | 
            +
            		rescue NoMethodError
         | 
| 113 | 
            +
            			# puts ' -- dm-validations gem not included. Cannot check subtypes for class.'
         | 
| 114 | 
            +
            			[]
         | 
| 115 | 
            +
            		end
         | 
| 116 | 
            +
            	end
         | 
| 117 | 
            +
             | 
| 118 | 
            +
            	def options_for_subtype(field)
         | 
| 119 | 
            +
            		self.validators.first.last.map{|a| a.options[:set] if a.class == DataMapper::Validate::WithinValidator && a.field_name == field}.compact.reduce
         | 
| 120 | 
            +
            	end
         | 
| 121 | 
            +
             | 
| 122 | 
            +
            	def relation_keys_include?(property)
         | 
| 123 | 
            +
            		self.relationships.map {|rel| true if property.to_sym == rel[1].child_key.first.name}.reduce
         | 
| 124 | 
            +
            	end
         | 
| 125 | 
            +
             | 
| 126 | 
            +
            end
         | 
| @@ -0,0 +1,122 @@ | |
| 1 | 
            +
            module Bowtie
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            	def self.models
         | 
| 4 | 
            +
            		models = MongoMapper::Document.descendants.to_a.uniq
         | 
| 5 | 
            +
            #		models.each {|m| models = models + m.subclasses}
         | 
| 6 | 
            +
            #		models
         | 
| 7 | 
            +
            	end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            	def self.search(params, page)
         | 
| 10 | 
            +
            		puts "Search not implemented yet in MongoMapper!"
         | 
| 11 | 
            +
            		return []
         | 
| 12 | 
            +
            	end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            	def self.get_many(model, params, page)
         | 
| 15 | 
            +
            		add_paging(model.where(params), page)
         | 
| 16 | 
            +
            	end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
            	def self.get_one(model, id)
         | 
| 19 | 
            +
            		model.find(id) 
         | 
| 20 | 
            +
            	end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
            	def self.create(model, params)
         | 
| 23 | 
            +
            		model.create(params)
         | 
| 24 | 
            +
            	end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
            	def self.get_associated(model, params)
         | 
| 27 | 
            +
            		model.find(params[:id]).send(params[:association])
         | 
| 28 | 
            +
            	end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
            	def self.add_paging(resources, page)
         | 
| 31 | 
            +
            		resources.skip((page.to_i || 0)*PER_PAGE).limit(PER_PAGE).sort('id asc')
         | 
| 32 | 
            +
            	end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
            	# doesnt trigger validations or callbacks
         | 
| 35 | 
            +
            	def self.update!(resource, params)
         | 
| 36 | 
            +
            		resource.update_attributes!(params)
         | 
| 37 | 
            +
            	end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
            	def self.update(resource, params)
         | 
| 40 | 
            +
            		resource.update_attributes(params)
         | 
| 41 | 
            +
            	end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
            	def self.belongs_to_association?(assoc)
         | 
| 44 | 
            +
            		assoc.class == MongoMapper::Plugins::Associations::BelongsToAssociation
         | 
| 45 | 
            +
            	end
         | 
| 46 | 
            +
            	
         | 
| 47 | 
            +
            	def self.has_one_association?(assoc)
         | 
| 48 | 
            +
            		assoc.class == MongoMapper::Plugins::Associations::OneAssociation
         | 
| 49 | 
            +
            	end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
            	module Helpers
         | 
| 52 | 
            +
             | 
| 53 | 
            +
            		def total_entries(resources)
         | 
| 54 | 
            +
            			resources.count
         | 
| 55 | 
            +
            		end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
            		def get_page(counter)
         | 
| 58 | 
            +
            			i = (params[:page].to_i || 0) + counter
         | 
| 59 | 
            +
            			i == 0 ? '' : "?page=#{i}"
         | 
| 60 | 
            +
            		end
         | 
| 61 | 
            +
             | 
| 62 | 
            +
            		def show_pager(resources, path)
         | 
| 63 | 
            +
            			path = base_path + path.gsub(/[?|&]page=\d+/,'') # remove page from path
         | 
| 64 | 
            +
            			nextlink = "<li><a class='next' href='#{path}#{get_page(1)}'>Next →</a></li>"
         | 
| 65 | 
            +
            			prevlink = "<li><a class='prev' href='#{path}#{get_page(-1)}'>← Prev</a></li>"
         | 
| 66 | 
            +
            			s = params[:page] ? prevlink + nextlink : nextlink
         | 
| 67 | 
            +
            			"<ul class='pager'>" + s + "</ul>"
         | 
| 68 | 
            +
            		end
         | 
| 69 | 
            +
             | 
| 70 | 
            +
            	end
         | 
| 71 | 
            +
             | 
| 72 | 
            +
            end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
            class Object
         | 
| 75 | 
            +
             | 
| 76 | 
            +
            	def primary_key
         | 
| 77 | 
            +
            		send(self.class.primary_key)
         | 
| 78 | 
            +
            	end
         | 
| 79 | 
            +
             | 
| 80 | 
            +
            end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
            class Class
         | 
| 83 | 
            +
             | 
| 84 | 
            +
            	def primary_key
         | 
| 85 | 
            +
            		'id'
         | 
| 86 | 
            +
            	end
         | 
| 87 | 
            +
             | 
| 88 | 
            +
            	def model_associations
         | 
| 89 | 
            +
            		associations
         | 
| 90 | 
            +
            	end
         | 
| 91 | 
            +
             | 
| 92 | 
            +
            	def field_names
         | 
| 93 | 
            +
            		self.keys.keys.collect { |f| f.to_sym }
         | 
| 94 | 
            +
            	end
         | 
| 95 | 
            +
             | 
| 96 | 
            +
            	def boolean_fields
         | 
| 97 | 
            +
            		s = []
         | 
| 98 | 
            +
            		self.keys.each {|k,v| s << k if v.type == Boolean}
         | 
| 99 | 
            +
            		s.compact
         | 
| 100 | 
            +
            	end
         | 
| 101 | 
            +
             | 
| 102 | 
            +
            	def searchable_fields
         | 
| 103 | 
            +
            		s = []
         | 
| 104 | 
            +
            		self.keys.each {|k,v| s << k if v.type == String}
         | 
| 105 | 
            +
            		s.compact
         | 
| 106 | 
            +
            	end
         | 
| 107 | 
            +
             | 
| 108 | 
            +
            	def subtypes
         | 
| 109 | 
            +
            		s = []
         | 
| 110 | 
            +
            		self.keys.each {|k,v| s << k if v.type.class == Array}
         | 
| 111 | 
            +
            		s.compact
         | 
| 112 | 
            +
            	end
         | 
| 113 | 
            +
             | 
| 114 | 
            +
            	def options_for_subtype(field)
         | 
| 115 | 
            +
            		self.keys[field].type
         | 
| 116 | 
            +
            	end
         | 
| 117 | 
            +
             | 
| 118 | 
            +
            	def relation_keys_include?(key)
         | 
| 119 | 
            +
            		self.associations.map {|rel| true if key.to_sym == rel[0]}.reduce
         | 
| 120 | 
            +
            	end
         | 
| 121 | 
            +
             | 
| 122 | 
            +
            end
         | 
    
        data/lib/bowtie/admin.rb
    CHANGED
    
    | @@ -1,8 +1,8 @@ | |
| 1 1 | 
             
            module Bowtie
         | 
| 2 2 |  | 
| 3 | 
            -
            	 | 
| 3 | 
            +
            	PER_PAGE = 25
         | 
| 4 4 |  | 
| 5 | 
            -
             | 
| 5 | 
            +
            	class Admin < Sinatra::Base
         | 
| 6 6 |  | 
| 7 7 | 
             
            		use Rack::Auth::Basic do |username, password|
         | 
| 8 8 | 
             
            			begin
         | 
| @@ -25,7 +25,7 @@ module Bowtie | |
| 25 25 |  | 
| 26 26 | 
             
            		before do
         | 
| 27 27 | 
             
            			@app_name = ENV['APP_NAME'] ? [self.class.name, ENV['APP_NAME']].join(' > ') : self.class.name
         | 
| 28 | 
            -
            			@models =  | 
| 28 | 
            +
            			@models = Bowtie.models
         | 
| 29 29 | 
             
            		end
         | 
| 30 30 |  | 
| 31 31 | 
             
            		get '/*.js|css|png|jpg' do
         | 
| @@ -34,30 +34,22 @@ module Bowtie | |
| 34 34 |  | 
| 35 35 | 
             
            		get '/' do
         | 
| 36 36 | 
             
            		  # redirect '' results in an endless redirect on the current version of sinatra/rack
         | 
| 37 | 
            -
            			redirect '/' + @models.first. | 
| 37 | 
            +
            			redirect '/' + @models.first.linkable
         | 
| 38 38 | 
             
            		end
         | 
| 39 39 |  | 
| 40 40 | 
             
            		get '' do
         | 
| 41 | 
            -
            			redirect '/' + @models.first. | 
| 41 | 
            +
            			redirect '/' + @models.first.linkable
         | 
| 42 42 | 
             
            		end
         | 
| 43 43 |  | 
| 44 44 | 
             
            		get '/search*' do
         | 
| 45 45 | 
             
            			redirect('/' + params[:model] ||= '') if params[:q].blank?
         | 
| 46 | 
            -
            			 | 
| 47 | 
            -
            			clean_params.each do |key, val|
         | 
| 48 | 
            -
            				query1 << "#{model}.all(:#{key} => '#{val}')"
         | 
| 49 | 
            -
            			end
         | 
| 50 | 
            -
            			model.searchable_fields.each do |field|
         | 
| 51 | 
            -
            				query2 << "#{model}.all(:#{field}.like => '%#{params[:q]}%')"
         | 
| 52 | 
            -
            			end
         | 
| 53 | 
            -
            			query = query1.any? ? [query1.join(' & '), query2.join(' + ')].join(' & ') : query2.join(' + ')
         | 
| 54 | 
            -
            			@resources = eval(query).page(params[:page], :per_page => PER_PAGE)
         | 
| 46 | 
            +
            			@resources = Bowtie.search(clean_params, params[:page])
         | 
| 55 47 | 
             
            			@subtypes = model.subtypes
         | 
| 56 48 | 
             
            			erb :index
         | 
| 57 49 | 
             
            		end
         | 
| 58 50 |  | 
| 59 51 | 
             
            		get "/:model" do
         | 
| 60 | 
            -
            			@resources =  | 
| 52 | 
            +
            			@resources = Bowtie.get_many(model, clean_params, params[:page])
         | 
| 61 53 | 
             
            			@subtypes = model.subtypes
         | 
| 62 54 | 
             
            			erb :index
         | 
| 63 55 | 
             
            		end
         | 
| @@ -68,9 +60,9 @@ module Bowtie | |
| 68 60 | 
             
            		end
         | 
| 69 61 |  | 
| 70 62 | 
             
            		post "/:model" do
         | 
| 71 | 
            -
            			@resource =  | 
| 63 | 
            +
            			@resource = Bowtie.create(model, params[:resource].prepare_for_query(model))
         | 
| 72 64 | 
             
            			if @resource.valid? and @resource.save
         | 
| 73 | 
            -
            				redirect "/#{model. | 
| 65 | 
            +
            				redirect "/#{model.linkable}?notice=created"
         | 
| 74 66 | 
             
            			else
         | 
| 75 67 | 
             
            				erb :new
         | 
| 76 68 | 
             
            			end
         | 
| @@ -83,27 +75,32 @@ module Bowtie | |
| 83 75 |  | 
| 84 76 | 
             
            		get "/:model/:id/:association" do
         | 
| 85 77 | 
             
            			@title = "#{params[:association].titleize} for #{model.to_s.titleize} ##{params[:id]}"
         | 
| 86 | 
            -
            			res =  | 
| 87 | 
            -
             | 
| 88 | 
            -
             | 
| 78 | 
            +
            			res = Bowtie.get_associated(model, params)
         | 
| 79 | 
            +
             | 
| 80 | 
            +
            			redirect('/' + model.linkable + '?error=doesnt+exist') if res.nil? or (res.is_a?(Array) and res.empty?)
         | 
| 81 | 
            +
             | 
| 82 | 
            +
            			if res.is_a?(Array)
         | 
| 83 | 
            +
            				@resources = Bowtie.add_paging(res, params[:page])
         | 
| 89 84 | 
             
            				erb :index
         | 
| 90 85 | 
             
            			else
         | 
| 91 | 
            -
            				redirect('/' + model.to_s + '?error=doesnt+exist') unless res
         | 
| 92 86 | 
             
            				@resource = res
         | 
| 93 87 | 
             
            				erb :show
         | 
| 94 88 | 
             
            			end
         | 
| 89 | 
            +
             | 
| 95 90 | 
             
            		end
         | 
| 96 91 |  | 
| 97 92 | 
             
            		put "/:model/:id" do
         | 
| 98 93 | 
             
            			if request.xhr? # dont pass through hooks or put the boolean stuff
         | 
| 99 | 
            -
            				if  | 
| 94 | 
            +
            				# if Bowtie.update!(resource, params[:resource].normalize)
         | 
| 95 | 
            +
            				puts params[:resource].inspect
         | 
| 96 | 
            +
            				if Bowtie.update!(resource, params[:resource].filter_inaccessible_in(model).normalize)
         | 
| 100 97 | 
             
            					resource.to_json
         | 
| 101 98 | 
             
            				else
         | 
| 102 99 | 
             
            					false
         | 
| 103 100 | 
             
            				end
         | 
| 104 101 | 
             
            			else # normal request
         | 
| 105 | 
            -
            				if  | 
| 106 | 
            -
            					redirect("/#{model. | 
| 102 | 
            +
            				if Bowtie.update(resource, params[:resource].prepare_for_query(model))
         | 
| 103 | 
            +
            					redirect("/#{model.linkable}/#{params[:id]}?notice=updated")
         | 
| 107 104 | 
             
            				else
         | 
| 108 105 | 
             
            					@resource = resource
         | 
| 109 106 | 
             
            					erb :show
         | 
| @@ -113,11 +110,12 @@ module Bowtie | |
| 113 110 |  | 
| 114 111 | 
             
            		delete "/:model/:id" do
         | 
| 115 112 | 
             
            			if resource.destroy
         | 
| 116 | 
            -
            				redirect "/#{model. | 
| 113 | 
            +
            				redirect "/#{model.linkable}?notice=destroyed"
         | 
| 117 114 | 
             
            			else
         | 
| 118 | 
            -
            				redirect "/#{model. | 
| 115 | 
            +
            				redirect "/#{model.linkable}/#{params[:id]}?error=not+destroyed"
         | 
| 119 116 | 
             
            			end
         | 
| 120 117 | 
             
            		end
         | 
| 118 | 
            +
             | 
| 121 119 | 
             
            	end
         | 
| 122 120 |  | 
| 123 121 | 
             
            end
         | 
| @@ -14,39 +14,35 @@ class String | |
| 14 14 |  | 
| 15 15 | 
             
            end
         | 
| 16 16 |  | 
| 17 | 
            -
             | 
| 18 | 
            -
             | 
| 19 | 
            -
            	def  | 
| 20 | 
            -
            		 | 
| 21 | 
            -
             | 
| 22 | 
            -
             | 
| 23 | 
            -
             | 
| 24 | 
            -
             | 
| 25 | 
            -
             | 
| 26 | 
            -
             | 
| 27 | 
            -
             | 
| 28 | 
            -
             | 
| 17 | 
            +
            module Subclasses
         | 
| 18 | 
            +
            	# return a list of the subclasses of a class
         | 
| 19 | 
            +
            	def subclasses(direct = false)
         | 
| 20 | 
            +
            		classes = []
         | 
| 21 | 
            +
            		if direct
         | 
| 22 | 
            +
            			ObjectSpace.each_object(Class) do |c|
         | 
| 23 | 
            +
            				next unless c.superclass == self
         | 
| 24 | 
            +
            				classes << c
         | 
| 25 | 
            +
            			end
         | 
| 26 | 
            +
            		else
         | 
| 27 | 
            +
            			ObjectSpace.each_object(Class) do |c|
         | 
| 28 | 
            +
            				next unless c.ancestors.include?(self) and (c != self)
         | 
| 29 | 
            +
            				classes << c
         | 
| 30 | 
            +
            			end
         | 
| 31 | 
            +
            		end
         | 
| 32 | 
            +
            		classes
         | 
| 29 33 | 
             
            	end
         | 
| 34 | 
            +
            end
         | 
| 30 35 |  | 
| 31 | 
            -
             | 
| 32 | 
            -
            		self.properties.map{|a| a.name if a.class == DataMapper::Property::String}.compact
         | 
| 33 | 
            -
            	end
         | 
| 36 | 
            +
            Object.send(:include, Subclasses)
         | 
| 34 37 |  | 
| 35 | 
            -
             | 
| 36 | 
            -
            		begin
         | 
| 37 | 
            -
            			self.validators.first.last.map{|a,b| b = {a.field_name => a.options[:set]} if a.class == DataMapper::Validate::WithinValidator}.compact
         | 
| 38 | 
            -
            		rescue NoMethodError
         | 
| 39 | 
            -
            			# puts ' -- dm-validations gem not included. Cannot check subtypes for class.'
         | 
| 40 | 
            -
            			[]
         | 
| 41 | 
            -
            		end
         | 
| 42 | 
            -
            	end
         | 
| 38 | 
            +
            class Class
         | 
| 43 39 |  | 
| 44 | 
            -
            	def  | 
| 45 | 
            -
            		self. | 
| 40 | 
            +
            	def linkable
         | 
| 41 | 
            +
            		self.to_s.downcase.pluralize
         | 
| 46 42 | 
             
            	end
         | 
| 47 43 |  | 
| 48 | 
            -
            	def  | 
| 49 | 
            -
             | 
| 44 | 
            +
            	def pluralize
         | 
| 45 | 
            +
            		 self.to_s.pluralize
         | 
| 50 46 | 
             
            	end
         | 
| 51 47 |  | 
| 52 48 | 
             
            end
         | 
| @@ -71,7 +67,7 @@ class Hash | |
| 71 67 |  | 
| 72 68 | 
             
            	# this is for checkboxes which give us a param of 'on' on the params hash
         | 
| 73 69 | 
             
            	def normalize
         | 
| 74 | 
            -
            		replacements = { 'on' => true, '' => nil}
         | 
| 70 | 
            +
            		replacements = { 'on' => true, '' => nil, 'true' => true, 'false' => false}
         | 
| 75 71 | 
             
            		normalized = {}
         | 
| 76 72 | 
             
            		self.each_pair do |key,val|
         | 
| 77 73 | 
             
            			normalized[key] = replacements.has_key?(val) ? replacements[val] : val
         | 
    
        data/lib/bowtie/helpers.rb
    CHANGED
    
    | @@ -38,7 +38,7 @@ module Bowtie | |
| 38 38 | 
             
            		end
         | 
| 39 39 |  | 
| 40 40 | 
             
            		def resource
         | 
| 41 | 
            -
            			 | 
| 41 | 
            +
            			Bowtie.get_one(model, params[:id]) or halt(404, 'Resource not found!')
         | 
| 42 42 | 
             
            		end
         | 
| 43 43 |  | 
| 44 44 | 
             
            		def current_model
         | 
| @@ -56,14 +56,34 @@ module Bowtie | |
| 56 56 | 
             
            			base_path + '/' + string.to_s.pluralize.downcase
         | 
| 57 57 | 
             
            		end
         | 
| 58 58 |  | 
| 59 | 
            -
            		def url_for( | 
| 60 | 
            -
            			model_path( | 
| 59 | 
            +
            		def url_for(resource)
         | 
| 60 | 
            +
            			model_path(resource.class) + '/' + resource.id.to_s
         | 
| 61 | 
            +
            		end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
            		def link_to(string, resource)
         | 
| 64 | 
            +
            			uri = resource.nil? ? "#" : url_for(resource)
         | 
| 65 | 
            +
            			"<a href='#{uri}'>#{string}</a>"
         | 
| 61 66 | 
             
            		end
         | 
| 62 67 |  | 
| 63 68 | 
             
            		def truncate(str, length)
         | 
| 64 69 | 
             
            			str.to_s.length > length ? str.to_s[0..length] + '…' : str.to_s
         | 
| 65 70 | 
             
            		end
         | 
| 66 71 |  | 
| 72 | 
            +
            		def render_assoc_header(rel_name, assoc)
         | 
| 73 | 
            +
            			"<th title='#{assoc.class.name.to_s[/.*::(.*)$/, 1]}' class='rel-col #{rel_name}-col'>#{rel_name.to_s.titleize}</th>"
         | 
| 74 | 
            +
            		end
         | 
| 75 | 
            +
            		
         | 
| 76 | 
            +
            		def render_assoc_row(r, rel_name, assoc)
         | 
| 77 | 
            +
            			html = "<td class='rel-col #{rel_name.to_s}-col'>"
         | 
| 78 | 
            +
            			html += "<a href='#{model_path}/#{r.id}/#{rel_name.to_s}'>"
         | 
| 79 | 
            +
            			if Bowtie.has_one_association?(assoc) || Bowtie.belongs_to_association?(assoc)
         | 
| 80 | 
            +
            				html += (r.send(rel_name).nil? ? 'nil' : "View #{rel_name.to_s}")
         | 
| 81 | 
            +
            			else
         | 
| 82 | 
            +
            				html += r.send(rel_name).count.to_s
         | 
| 83 | 
            +
            			end
         | 
| 84 | 
            +
            			html += "</a></td>"
         | 
| 85 | 
            +
            		end
         | 
| 86 | 
            +
             | 
| 67 87 | 
             
            	end
         | 
| 68 88 |  | 
| 69 89 | 
             
            end
         | 
| @@ -86,14 +86,47 @@ h2 { margin:10px 0; font-size:130%;} | |
| 86 86 | 
             
            	-moz-border-top-left-radius: 6px;
         | 
| 87 87 | 
             
            }
         | 
| 88 88 |  | 
| 89 | 
            -
            #header ul li a:hover {  | 
| 90 | 
            -
             | 
| 89 | 
            +
            #header ul li a:hover { 
         | 
| 90 | 
            +
            	background:#333;
         | 
| 91 | 
            +
            }
         | 
| 92 | 
            +
             | 
| 93 | 
            +
            #header ul li.current a { 
         | 
| 94 | 
            +
            	background: #528e00; 
         | 
| 95 | 
            +
            	font-weight:bold; 
         | 
| 96 | 
            +
            	color:#fff;
         | 
| 97 | 
            +
            }
         | 
| 98 | 
            +
             | 
| 99 | 
            +
            .subnav { 
         | 
| 100 | 
            +
            	padding: 2px 50px 7px 50px; 
         | 
| 101 | 
            +
            	background:#528e00; 
         | 
| 102 | 
            +
            	font-size:90%; 
         | 
| 103 | 
            +
            	margin: -20px -50px 20px; 
         | 
| 104 | 
            +
            }
         | 
| 91 105 |  | 
| 92 | 
            -
            .subnav {  | 
| 93 | 
            -
             | 
| 94 | 
            -
             | 
| 95 | 
            -
             | 
| 96 | 
            -
            .subnav li a | 
| 106 | 
            +
            .subnav li { 
         | 
| 107 | 
            +
            	display:inline;
         | 
| 108 | 
            +
            }
         | 
| 109 | 
            +
             | 
| 110 | 
            +
            .subnav li a { 
         | 
| 111 | 
            +
            	color:#fff; 
         | 
| 112 | 
            +
            	text-decoration:none; 
         | 
| 113 | 
            +
            	margin-right:10px; 
         | 
| 114 | 
            +
            	display:inline-block; 
         | 
| 115 | 
            +
            	background:#89bf41; 
         | 
| 116 | 
            +
            	padding:5px; 
         | 
| 117 | 
            +
            	-webkit-border-radius:3px; 
         | 
| 118 | 
            +
            	-moz-border-radius:3px;
         | 
| 119 | 
            +
            }
         | 
| 120 | 
            +
             | 
| 121 | 
            +
            .subnav li.current a { 
         | 
| 122 | 
            +
            	background: #fff; 
         | 
| 123 | 
            +
            	font-weight:bold; 
         | 
| 124 | 
            +
            	color:#528e00;
         | 
| 125 | 
            +
            }
         | 
| 126 | 
            +
             | 
| 127 | 
            +
            .subnav li a:active { 
         | 
| 128 | 
            +
            	background:#8def08;
         | 
| 129 | 
            +
            }
         | 
| 97 130 |  | 
| 98 131 | 
             
            /* container
         | 
| 99 132 | 
             
            -------------------------------------------------------------*/
         | 
| @@ -183,6 +216,12 @@ table .check-column{ | |
| 183 216 | 
             
            	text-align: center;
         | 
| 184 217 | 
             
            }
         | 
| 185 218 |  | 
| 219 | 
            +
            table .view-col,
         | 
| 220 | 
            +
            table .delete-col{
         | 
| 221 | 
            +
            	text-align: center;
         | 
| 222 | 
            +
            	width: 100px;
         | 
| 223 | 
            +
            }
         | 
| 224 | 
            +
             | 
| 186 225 | 
             
            /* good states
         | 
| 187 226 | 
             
            ---------------------------*/
         | 
| 188 227 |  | 
| @@ -227,12 +266,14 @@ table tr.suspended td{ | |
| 227 266 | 
             
            	background-color: #FFEFEF;
         | 
| 228 267 | 
             
            }
         | 
| 229 268 |  | 
| 230 | 
            -
            table th.rel{
         | 
| 269 | 
            +
            table th.rel-col{
         | 
| 231 270 | 
             
            	background: #999;
         | 
| 232 271 | 
             
            	color: #fff;
         | 
| 272 | 
            +
            	text-align: center;
         | 
| 233 273 | 
             
            }
         | 
| 234 | 
            -
            table td.rel {
         | 
| 274 | 
            +
            table td.rel-col {
         | 
| 235 275 | 
             
            	background: #eee;
         | 
| 276 | 
            +
            	text-align: center;
         | 
| 236 277 | 
             
            }
         | 
| 237 278 |  | 
| 238 279 | 
             
            table .icon{
         | 
    
        data/lib/bowtie/views/errors.erb
    CHANGED
    
    
    
        data/lib/bowtie/views/field.erb
    CHANGED
    
    | @@ -1,9 +1,9 @@ | |
| 1 1 | 
             
            <%=
         | 
| 2 2 |  | 
| 3 3 | 
             
            	html =''
         | 
| 4 | 
            -
            	name = @p | 
| 4 | 
            +
            	name = @p
         | 
| 5 5 | 
             
            	begin
         | 
| 6 | 
            -
            		value = @resource.send(@p | 
| 6 | 
            +
            		value = @resource.send(@p)
         | 
| 7 7 | 
             
            	rescue NoMethodError
         | 
| 8 8 | 
             
            		value = ''
         | 
| 9 9 | 
             
            	end
         | 
| @@ -20,7 +20,7 @@ | |
| 20 20 | 
             
            			checked = value == true ? 'checked="checked"' : ''
         | 
| 21 21 | 
             
            			html += '<input name="resource['+name+']" type="checkbox" '+checked+' />'
         | 
| 22 22 | 
             
            		else
         | 
| 23 | 
            -
            			html += '<input class="string" type="text" name="resource['+name+']" value="'+value.to_s+'" />'
         | 
| 23 | 
            +
            			html += '<input class="string" type="text" name="resource['+name.to_s+']" value="'+value.to_s+'" />'
         | 
| 24 24 | 
             
            	end
         | 
| 25 25 | 
             
            	html
         | 
| 26 26 |  | 
    
        data/lib/bowtie/views/form.erb
    CHANGED
    
    | @@ -1,22 +1,23 @@ | |
| 1 | 
            -
            <form class="resource" method="post" action="<%= model_path(@resource. | 
| 1 | 
            +
            <form class="resource" method="post" action="<%= model_path(@resource.class) %><%= '/' + @resource.id.to_s if @resource.id %>">
         | 
| 2 2 | 
             
            <%= '<input type="hidden" name="_method" value="put">' if @resource.id %>
         | 
| 3 3 |  | 
| 4 4 | 
             
            <%= partial(:errors) %>
         | 
| 5 5 |  | 
| 6 | 
            -
            <table>
         | 
| 7 | 
            -
             | 
| 8 | 
            -
            	 | 
| 9 | 
            -
            	<% @ | 
| 6 | 
            +
            	<table>
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            	<!-- filter id and created at fields. association ids pass -->
         | 
| 9 | 
            +
            	<% @resource.class.field_names.each do |@p| %>
         | 
| 10 | 
            +
            	<% next if %w(id _id created_at _created_at _type).include?(@p.to_s) %>
         | 
| 10 11 | 
             
            	<tr>
         | 
| 11 | 
            -
            		<td class="left-col"><%= p. | 
| 12 | 
            +
            		<td class="left-col"><%= @p.to_s.titleize %> <small>(<%= @p.class.name.to_s.gsub("DataMapper::Property::",'') %>)</small></td>
         | 
| 12 13 | 
             
            		<td class="right-col"><%= partial(:field) %></td>
         | 
| 13 14 | 
             
            	</tr>
         | 
| 14 15 | 
             
            	<% end %>
         | 
| 15 | 
            -
            	<% end %>
         | 
| 16 | 
            -
            </table>
         | 
| 17 16 |  | 
| 18 | 
            -
             | 
| 19 | 
            -
             | 
| 20 | 
            -
             | 
| 17 | 
            +
            	</table>
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            	<p class="submit">
         | 
| 20 | 
            +
            		<input type="submit" value="Submit" />
         | 
| 21 | 
            +
            	</p>
         | 
| 21 22 |  | 
| 22 23 | 
             
            </form>
         | 
    
        data/lib/bowtie/views/index.erb
    CHANGED
    
    | @@ -3,15 +3,15 @@ | |
| 3 3 | 
             
            <%= partial(:search) %>
         | 
| 4 4 | 
             
            <a class="edit-button" href="#" title="Enables in-place editing of editable fields" onclick="Bowtie.toggleEditableMode(this); return false;">Edit Mode OFF</a>
         | 
| 5 5 |  | 
| 6 | 
            -
            <h1><%= @resources | 
| 6 | 
            +
            <h1><%= total_entries(@resources) %> <%= @title || @model.name.pluralize %> <a href="<%= model_path %>/new">(new)</a></h1>
         | 
| 7 7 |  | 
| 8 8 | 
             
            <% if @resources.any? %>
         | 
| 9 9 |  | 
| 10 | 
            -
            	<%= @resources | 
| 10 | 
            +
            	<%= show_pager(@resources, request.path_info) %>
         | 
| 11 11 |  | 
| 12 12 | 
             
            	<%= partial(:table) %>
         | 
| 13 13 |  | 
| 14 | 
            -
            	<%= @resources | 
| 14 | 
            +
            	<%= show_pager(@resources, request.path_info) %>
         | 
| 15 15 |  | 
| 16 16 | 
             
            <% else %>
         | 
| 17 17 |  | 
    
        data/lib/bowtie/views/row.erb
    CHANGED
    
    | @@ -1,25 +1,24 @@ | |
| 1 | 
            -
            <tr id="resource-<%= @r. | 
| 2 | 
            -
            	<td class="<%= @model. | 
| 3 | 
            -
             | 
| 4 | 
            -
            	<%  | 
| 5 | 
            -
             | 
| 1 | 
            +
            <tr id="resource-<%= @r.primary_key.to_s %>" class="<%= @r.state.to_s if @r.respond_to?(:state) %>">
         | 
| 2 | 
            +
            	<td class="<%= @model.primary_key %>-col"><a href="<%= url_for(@r) %>"><%= @r.primary_key %></a></td>
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            	<% @r.attributes.each do |k, v| %>
         | 
| 5 | 
            +
            		<% next if k == @model.primary_key or k.to_s[/id$/] or k == '_type' %>
         | 
| 6 | 
            +
            		<td class="<%= k %>-col <%= k.class.name.downcase %> editable"><%= truncate(v, 64) || 'nil' %></td>
         | 
| 6 7 | 
             
            	<% end %>
         | 
| 8 | 
            +
             | 
| 7 9 | 
             
            	<% unless params[:association] %>
         | 
| 8 | 
            -
            	<% @model. | 
| 9 | 
            -
            		 | 
| 10 | 
            -
            		<td class="rel <%= rel[0].to_s %>-col">
         | 
| 11 | 
            -
            			<a href="<%= model_path %>/<%= @r.id %>/<%= rel[0].to_s %>">
         | 
| 12 | 
            -
            				<%= rel[1].class.name =~ /OneToOne/ ? "View" : @r.send(rel[0]).count %>
         | 
| 13 | 
            -
            			</a>
         | 
| 14 | 
            -
            		</td>
         | 
| 15 | 
            -
            		<% end %>
         | 
| 10 | 
            +
            	<% @model.model_associations.each do |rel_name, assoc| %>
         | 
| 11 | 
            +
            		<%= render_assoc_row(@r, rel_name, assoc) %>
         | 
| 16 12 | 
             
            	<% end %>
         | 
| 17 13 | 
             
            	<% end %>
         | 
| 18 | 
            -
             | 
| 19 | 
            -
            	<td>
         | 
| 20 | 
            -
             | 
| 14 | 
            +
             | 
| 15 | 
            +
            	<td class="view-col"><a href="<%= url_for(@r) %>">View</a></td>
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            	<td class="delete-col">
         | 
| 18 | 
            +
            	<form class="destroy" method="post" action="<%= model_path(@r.class) %>/<%= @r.id %>" onsubmit="return confirm('Are you sure?');">
         | 
| 21 19 | 
             
            		<input type="hidden" name="_method" value="delete" />
         | 
| 22 20 | 
             
            		<button type="submit">Destroy</button>
         | 
| 23 21 | 
             
            	</form>
         | 
| 24 22 | 
             
            	</td>
         | 
| 23 | 
            +
             | 
| 25 24 | 
             
            </tr>
         | 
    
        data/lib/bowtie/views/search.erb
    CHANGED
    
    | @@ -1,8 +1,8 @@ | |
| 1 1 | 
             
            	<% model = params[:model] %>
         | 
| 2 2 | 
             
            	<form id="search" action="<%= base_path %>/search" method="get">
         | 
| 3 3 | 
             
            		<% params.each do |k, v| %>
         | 
| 4 | 
            -
            		<% next if ['splat', 'q', 'page'].include?(k) or v | 
| 4 | 
            +
            		<% next if ['splat', 'q', 'page'].include?(k) or v == '' %>
         | 
| 5 5 | 
             
            			<input type="hidden" name="<%= k %>" value="<%= v %>" />
         | 
| 6 6 | 
             
            		<% end %>
         | 
| 7 | 
            -
            		<input type="text" name="q"  value="Find  | 
| 7 | 
            +
            		<input type="text" name="q"  value="<%= params[:q] || 'Find ' + model + ':' %>" onfocus="if(this.value=='Find <%= model %>:')this.value='';" onblur="if(this.value=='')this.value='Find <%= model %>:';"/>
         | 
| 8 8 | 
             
            	</form>
         | 
    
        data/lib/bowtie/views/show.erb
    CHANGED
    
    | @@ -1,6 +1,6 @@ | |
| 1 1 | 
             
            <h1><%= @title || @model.name + ':' + @resource.id.to_s %></h1>
         | 
| 2 2 |  | 
| 3 | 
            -
            <form class="big destroy" method="post" action="<%= model_path(@resource. | 
| 3 | 
            +
            <form class="big destroy" method="post" action="<%= model_path(@resource.class) %>/<%= @resource.id %>" onsubmit="return confirm('Are you sure?');">
         | 
| 4 4 | 
             
            	<input type="hidden" name="_method" value="delete" />
         | 
| 5 5 | 
             
            	<button type="submit">Destroy</button>
         | 
| 6 6 | 
             
            </form>
         | 
    
        data/lib/bowtie/views/table.erb
    CHANGED
    
    | @@ -1,25 +1,28 @@ | |
| 1 1 | 
             
            	<table class="sortable">
         | 
| 2 2 | 
             
            		<thead>
         | 
| 3 3 | 
             
            			<tr>
         | 
| 4 | 
            -
            			<th title="<%= @model. | 
| 5 | 
            -
             | 
| 6 | 
            -
            			 | 
| 7 | 
            -
            			 | 
| 8 | 
            -
             | 
| 9 | 
            -
             | 
| 10 | 
            -
            		<% @resources.first.class.relationships.each do |rel| %>
         | 
| 11 | 
            -
            			<% unless rel[1].class.name =~ /ManyTo/ %>
         | 
| 12 | 
            -
            			<th title="<%= rel[1].class.name.to_s.gsub('DataMapper::Associations::','') %>" class="rel <%= rel[0].to_s %>-col"><%= rel[0].to_s.titleize %></th>
         | 
| 4 | 
            +
            			<th title="<%= @model.primary_key %>" class="<%= @model.primary_key %>-col"><%= @model.primary_key.to_s.upcase %></th>
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            			<!-- filter id, _type and associations -->
         | 
| 7 | 
            +
            			<% @resources.first.attributes.each do |k, v| %>
         | 
| 8 | 
            +
            				<% next if k.to_s == 'id' or k.to_s[/id$/] or k == '_type' or v.class == Array %>
         | 
| 9 | 
            +
            				<th title="<%= v.class %>" class="<%= k.to_s %>-col <%= v.class.name.downcase %>"><%= k.to_s.titleize %></th>
         | 
| 13 10 | 
             
            			<% end %>
         | 
| 14 | 
            -
             | 
| 15 | 
            -
             | 
| 16 | 
            -
            			 | 
| 11 | 
            +
             | 
| 12 | 
            +
            			<% unless params[:association] %>
         | 
| 13 | 
            +
            			<% @resources.first.class.model_associations.each do |rel_name, assoc| %>
         | 
| 14 | 
            +
            				<%= render_assoc_header(rel_name, assoc) %>
         | 
| 15 | 
            +
            			<% end %>
         | 
| 16 | 
            +
            			<% end %>
         | 
| 17 | 
            +
             | 
| 18 | 
            +
            			<th class="actions" colspan="2"> </th>
         | 
| 17 19 | 
             
            			</tr>
         | 
| 18 20 | 
             
            		</thead>
         | 
| 21 | 
            +
             | 
| 19 22 | 
             
            		<tbody>
         | 
| 20 | 
            -
            		<% @resources.each do  | 
| 21 | 
            -
            			<% @r = r %>
         | 
| 23 | 
            +
            		<% @resources.each do |@r| %>
         | 
| 22 24 | 
             
            			<%= partial(:row) %>
         | 
| 23 25 | 
             
            		<% end %>
         | 
| 24 26 | 
             
            		</tbody>
         | 
| 27 | 
            +
             | 
| 25 28 | 
             
            	</table>
         | 
    
        metadata
    CHANGED
    
    | @@ -1,13 +1,12 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification 
         | 
| 2 2 | 
             
            name: bowtie
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version 
         | 
| 4 | 
            -
              hash:  | 
| 4 | 
            +
              hash: 3
         | 
| 5 5 | 
             
              prerelease: 
         | 
| 6 6 | 
             
              segments: 
         | 
| 7 7 | 
             
              - 0
         | 
| 8 | 
            -
              -  | 
| 9 | 
            -
               | 
| 10 | 
            -
              version: 0.3.2
         | 
| 8 | 
            +
              - 4
         | 
| 9 | 
            +
              version: "0.4"
         | 
| 11 10 | 
             
            platform: ruby
         | 
| 12 11 | 
             
            authors: 
         | 
| 13 12 | 
             
            - "Tom\xC3\xA1s Pollak"
         | 
| @@ -34,55 +33,7 @@ dependencies: | |
| 34 33 | 
             
                    version: 0.9.4
         | 
| 35 34 | 
             
              type: :runtime
         | 
| 36 35 | 
             
              version_requirements: *id001
         | 
| 37 | 
            -
             | 
| 38 | 
            -
              name: dm-core
         | 
| 39 | 
            -
              prerelease: false
         | 
| 40 | 
            -
              requirement: &id002 !ruby/object:Gem::Requirement 
         | 
| 41 | 
            -
                none: false
         | 
| 42 | 
            -
                requirements: 
         | 
| 43 | 
            -
                - - ">="
         | 
| 44 | 
            -
                  - !ruby/object:Gem::Version 
         | 
| 45 | 
            -
                    hash: 51
         | 
| 46 | 
            -
                    segments: 
         | 
| 47 | 
            -
                    - 0
         | 
| 48 | 
            -
                    - 10
         | 
| 49 | 
            -
                    - 2
         | 
| 50 | 
            -
                    version: 0.10.2
         | 
| 51 | 
            -
              type: :runtime
         | 
| 52 | 
            -
              version_requirements: *id002
         | 
| 53 | 
            -
            - !ruby/object:Gem::Dependency 
         | 
| 54 | 
            -
              name: dm-aggregates
         | 
| 55 | 
            -
              prerelease: false
         | 
| 56 | 
            -
              requirement: &id003 !ruby/object:Gem::Requirement 
         | 
| 57 | 
            -
                none: false
         | 
| 58 | 
            -
                requirements: 
         | 
| 59 | 
            -
                - - ">="
         | 
| 60 | 
            -
                  - !ruby/object:Gem::Version 
         | 
| 61 | 
            -
                    hash: 51
         | 
| 62 | 
            -
                    segments: 
         | 
| 63 | 
            -
                    - 0
         | 
| 64 | 
            -
                    - 10
         | 
| 65 | 
            -
                    - 2
         | 
| 66 | 
            -
                    version: 0.10.2
         | 
| 67 | 
            -
              type: :runtime
         | 
| 68 | 
            -
              version_requirements: *id003
         | 
| 69 | 
            -
            - !ruby/object:Gem::Dependency 
         | 
| 70 | 
            -
              name: dm-pager
         | 
| 71 | 
            -
              prerelease: false
         | 
| 72 | 
            -
              requirement: &id004 !ruby/object:Gem::Requirement 
         | 
| 73 | 
            -
                none: false
         | 
| 74 | 
            -
                requirements: 
         | 
| 75 | 
            -
                - - ">="
         | 
| 76 | 
            -
                  - !ruby/object:Gem::Version 
         | 
| 77 | 
            -
                    hash: 21
         | 
| 78 | 
            -
                    segments: 
         | 
| 79 | 
            -
                    - 1
         | 
| 80 | 
            -
                    - 0
         | 
| 81 | 
            -
                    - 1
         | 
| 82 | 
            -
                    version: 1.0.1
         | 
| 83 | 
            -
              type: :runtime
         | 
| 84 | 
            -
              version_requirements: *id004
         | 
| 85 | 
            -
            description: Admin scaffold for DataMapper models, on Sinatra.
         | 
| 36 | 
            +
            description: Simple admin scaffold for MongoMapper and DataMapper models.
         | 
| 86 37 | 
             
            email: tomas@forkhq.com
         | 
| 87 38 | 
             
            executables: []
         | 
| 88 39 |  | 
| @@ -94,9 +45,11 @@ extra_rdoc_files: | |
| 94 45 | 
             
            - lib/bowtie/core_extensions.rb
         | 
| 95 46 | 
             
            - lib/bowtie/helpers.rb
         | 
| 96 47 | 
             
            files: 
         | 
| 97 | 
            -
            - README. | 
| 48 | 
            +
            - README.md
         | 
| 98 49 | 
             
            - bowtie.gemspec
         | 
| 99 50 | 
             
            - lib/bowtie.rb
         | 
| 51 | 
            +
            - lib/bowtie/adapters/datamapper.rb
         | 
| 52 | 
            +
            - lib/bowtie/adapters/mongomapper.rb
         | 
| 100 53 | 
             
            - lib/bowtie/admin.rb
         | 
| 101 54 | 
             
            - lib/bowtie/core_extensions.rb
         | 
| 102 55 | 
             
            - lib/bowtie/helpers.rb
         | 
| @@ -155,6 +108,6 @@ rubyforge_project: bowtie | |
| 155 108 | 
             
            rubygems_version: 1.5.2
         | 
| 156 109 | 
             
            signing_key: 
         | 
| 157 110 | 
             
            specification_version: 3
         | 
| 158 | 
            -
            summary: Bowtie Admin
         | 
| 111 | 
            +
            summary: Bowtie Admin Scaffold
         | 
| 159 112 | 
             
            test_files: []
         | 
| 160 113 |  | 
    
        data/README.rdoc
    DELETED
    
    | @@ -1,48 +0,0 @@ | |
| 1 | 
            -
            = Bowtie for Sinatra.
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            Zeroconf admin generator for DataMapper models, written in Sinatra.
         | 
| 4 | 
            -
             | 
| 5 | 
            -
            = What it does
         | 
| 6 | 
            -
             | 
| 7 | 
            -
            Reads the information on your models and creates a nice panel in which you can view, edit and destroy records easily.
         | 
| 8 | 
            -
             | 
| 9 | 
            -
            = Installation
         | 
| 10 | 
            -
             | 
| 11 | 
            -
              $ sudo gem install bowtie
         | 
| 12 | 
            -
             | 
| 13 | 
            -
            = Important
         | 
| 14 | 
            -
             | 
| 15 | 
            -
            From version 0.3, Bowtie is meant to be used from DataMapper 1.0.0 on. For previous versions please install with -v=0.2.5.
         | 
| 16 | 
            -
             | 
| 17 | 
            -
            = Configuration
         | 
| 18 | 
            -
             | 
| 19 | 
            -
            Mount Bowtie wherever you want by editing your config.ru file, optionally including the admin/pass combination for the panel.
         | 
| 20 | 
            -
             | 
| 21 | 
            -
              require 'myapp'
         | 
| 22 | 
            -
              require 'bowtie'
         | 
| 23 | 
            -
             | 
| 24 | 
            -
              BOWTIE_AUTH = {:user => 'admin', :pass => '12345' }
         | 
| 25 | 
            -
             | 
| 26 | 
            -
              app = Rack::Builder.new {
         | 
| 27 | 
            -
                map "/admin" do
         | 
| 28 | 
            -
                  run Bowtie::Admin
         | 
| 29 | 
            -
                end
         | 
| 30 | 
            -
             | 
| 31 | 
            -
                map '/' do
         | 
| 32 | 
            -
                  run MyApp
         | 
| 33 | 
            -
                end
         | 
| 34 | 
            -
              }
         | 
| 35 | 
            -
             | 
| 36 | 
            -
              run app
         | 
| 37 | 
            -
             | 
| 38 | 
            -
            Now you can go to /admin in your app's path and try out Bowtie using your user/pass combination. If not set, it defaults to admin/bowtie.
         | 
| 39 | 
            -
             | 
| 40 | 
            -
            = TODO
         | 
| 41 | 
            -
             | 
| 42 | 
            -
             * Better handling of types (Text, JSON, IPAddress) in #show
         | 
| 43 | 
            -
             * Better handling of relationships in #show
         | 
| 44 | 
            -
             | 
| 45 | 
            -
            = Copyright
         | 
| 46 | 
            -
             | 
| 47 | 
            -
            (c) 2010 - Tomás Pollak for Fork Ltd.
         | 
| 48 | 
            -
            Released under the MIT license.
         |