padrino-admin 0.2.9 → 0.4.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,167 @@
1
+ module Padrino
2
+ module ExtJs
3
+ # Return column config, and store config/data for ExtJS ColumnModel and Store
4
+ #
5
+ # Examples:
6
+ #
7
+ # # app/controllers/backend/debtors_controller.rb
8
+ # def index
9
+ # @column_store = column_store_for Debtor do |cm|
10
+ # cm.add :id
11
+ # cm.add "full_name_or_company.upcase", "Full Name", :sortable => true, :dataIndex => :company
12
+ # cm.add :surname # Header will be autogenerated
13
+ # cm.add :email, "Email", :sortable => true
14
+ # cm.add :piva, "Piva", :sortable => true
15
+ # cm.add :created_at, "Creato il", :sortable => true, :renderer => :date, :align => :right
16
+ # cm.add :updated_at, "Aggiornato il", :sortable => true, :renderer => :datetime, :align => :right
17
+ # end
18
+ #
19
+ # respond_to do |format|
20
+ # format.js
21
+ # format.json do
22
+ # render :json => @column_store.store_data(params)
23
+ #
24
+ # # or you can manually do:
25
+ # # debtors = Debtor.search(params)
26
+ # # debtors_count = debtors.size
27
+ # # debtors_paginated = debtors.paginate(params)
28
+ # # render :json => { :results => @column_store.store_data_from(debtors_paginated), :count => debtors_count }
29
+ # end
30
+ # end
31
+ # end
32
+ #
33
+ # # app/views/backend/index.rjs
34
+ # page.grid do |grid|
35
+ # grid.id "debtors-grid" # If you don't set this columns are not saved in cookies
36
+ # grid.title "List al debtors"
37
+ # grid.base_path "/backend/debtors"
38
+ # grid.forgery_protection_token request_forgery_protection_token
39
+ # grid.authenticity_token form_authenticity_token
40
+ # grid.tbar :default
41
+ # grid.store do |store|
42
+ # store.url "/backend/debtors.json"
43
+ # store.fields @column_store.store_fields
44
+ # end
45
+ # grid.columns do |columns|
46
+ # columns.fields @column_store.column_fields
47
+ # end
48
+ # grid.bbar :store => grid.get_store, :pageSize => params[:limit] # Remember to add after defining store!
49
+ # end
50
+ #
51
+ module Controller
52
+ def self.column_store_for(model, &block)
53
+ ColumnStore.new(model, &block)
54
+ end
55
+
56
+ class ColumnStore #:nodoc:
57
+ attr_reader :data
58
+
59
+ def initialize(model, &block) #:nodoc
60
+ @model = model
61
+ @data = []
62
+ yield self
63
+ end
64
+
65
+ # Method for add columns to the Column Model
66
+ def add(*args)
67
+
68
+ # First we need to check that our method is a symbol
69
+ raise Padrino::ExtJs::ConfigError, "First args must be a symbol like: :name or :account.name" unless args[0].is_a?(Symbol)
70
+
71
+ # Construct our options
72
+ options = { :method => args[0] }
73
+
74
+ # If we have a second args that is not an hash maybe an header
75
+ options[:header] = args[1].is_a?(String) || args[1].is_a?(Symbol) ? args[1].to_s : nil
76
+
77
+ args.each { |a| options.merge!(a) if a.is_a?(Hash) }
78
+
79
+ # Add some defaults
80
+ options[:header] ||= options[:method].to_s
81
+ options[:sortable] = options[:sortable].nil? ? true : options[:sortable]
82
+
83
+ # Try to translate header
84
+ options[:header] = @model.human_attribute_name(options[:header].to_s)
85
+
86
+ # Reformat DataIndex
87
+ #
88
+ # If we don't have nothing we use the method
89
+ options[:dataIndex] ||= options[:method]
90
+
91
+ data_indexes = Array(options[:dataIndex]).collect do |data_index|
92
+ case data_index
93
+ when String then data_index
94
+ when Symbol
95
+ if data_index.missing_methods.size == 1
96
+ options[:name] ||= "#{@model.table_name.singularize}[#{data_index}]"
97
+ data_index = "#{@model.table_name}.#{data_index}"
98
+ else
99
+ columns = data_index.missing_methods
100
+ options[:name] ||= columns.at(0) + "[" + columns[1..-1].collect(&:to_s).join("][") + "]"
101
+ data_index = columns[0..-2].collect { |c| c.to_s.pluralize }.join(".") + "." + columns.at(-1).to_s
102
+ end
103
+ end
104
+ data_index
105
+ end
106
+
107
+ options[:dataIndex] = data_indexes.compact.uniq.join(",")
108
+
109
+ # Reformat mapping like a div id
110
+ options[:mapping] ||= options[:name].sub(/\[/,"_").sub(/\]$/, "").sub(/\]\[/,"_")
111
+
112
+ # Now is necessary for our columns an ID
113
+ # TODO: check duplicates here
114
+ options[:id] = options[:mapping]
115
+
116
+ @data << options
117
+ end
118
+
119
+ # Return an array config for build an Ext.grid.ColumnModel() config
120
+ def column_fields
121
+ @data.map do |data|
122
+ data.delete(:method)
123
+ data.delete(:mapping)
124
+ data
125
+ end
126
+ end
127
+
128
+ # Return an array config for build an Ext.data.GroupingStore()
129
+ def store_fields
130
+ @data.map do |data|
131
+ hash = { :name => data[:dataIndex], :mapping => data[:mapping] }
132
+ hash.merge!(:type => data[:renderer]) if data[:renderer] && [:date, :datetime, :time_to_date].include?(data[:renderer])
133
+ hash
134
+ end
135
+ end
136
+
137
+ # Return data for a custom collection for the ExtJS Ext.data.GroupingStore() json
138
+ def store_data_from(collection)
139
+ collection.map do |c|
140
+ @data.dup.inject({ "id" => c.id }) do |options, data|
141
+ options[data[:mapping]] = (c.instance_eval(data[:method].to_s) rescue I18n.t("padrino.admin.labels.not_found"))
142
+ options
143
+ end
144
+ end
145
+ end
146
+
147
+ # Return a searched and paginated data collection for the ExtJS Ext.data.GroupingStore() json
148
+ # You can pass options like:
149
+ #
150
+ # Examples
151
+ #
152
+ # store_data(params, :conditions => "found = 1")
153
+ # store_data(params, :include => :posts)
154
+ #
155
+ def store_data(params={}, options={})
156
+ # Some can tell me that this method made two identical queries one for count one for paginate.
157
+ # We don't use the select count because in some circumstances require much time than select *.
158
+ collection = @model.all(options).ext_search(params)
159
+ collection_count = collection.length
160
+ collection_paginated = collection.ext_paginate(params)
161
+ { :results => store_data_from(collection_paginated), :count => collection_count }
162
+ end
163
+
164
+ end
165
+ end
166
+ end
167
+ end
@@ -1,5 +1,5 @@
1
1
  require 'thor'
2
-
2
+ require 'padrino-gen' unless defined?(Padrino::Generators)
3
3
  module Padrino
4
4
  module Generators
5
5
 
@@ -0,0 +1,7 @@
1
+ en:
2
+ padrino:
3
+ admin:
4
+ labels:
5
+ not_found: "Not found"
6
+ buttons:
7
+ save: "Save"
@@ -0,0 +1,12 @@
1
+ class Symbol
2
+
3
+ def method_missing(method, *args)
4
+ super and return if method.to_s =~ /table_name/
5
+ (self.to_s + ".#{method}(#{args.collect(&:inspect).join(",")})").to_sym
6
+ end
7
+
8
+ def missing_methods
9
+ self.to_s.gsub(/(\(.*\))/,"").split(".")
10
+ end
11
+
12
+ end
@@ -0,0 +1,29 @@
1
+ module Padrino
2
+ module Admin
3
+ module Utils
4
+ # This util it's used for encrypt/decrypt password.
5
+ # We want password decryptable because generally for our sites we have: password_lost.
6
+ # We prefer send original password instead reset them.
7
+ module Crypt
8
+ # Decrypts the current string using the current key and algorithm specified
9
+ def decrypt(password)
10
+ cipher = build_cipher(:decrypt, password)
11
+ cipher.update(self.unpack('m')[0]) + cipher.final
12
+ end
13
+
14
+ # Encrypts the current string using the current key and algorithm specified
15
+ def encrypt(password)
16
+ cipher = build_cipher(:encrypt, password)
17
+ [cipher.update(self) + cipher.final].pack('m').chomp
18
+ end
19
+
20
+ private
21
+ def build_cipher(type, password) #:nodoc:
22
+ cipher = OpenSSL::Cipher::Cipher.new("DES-EDE3-CBC").send(type)
23
+ cipher.pkcs5_keyivgen(password)
24
+ cipher
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{padrino-admin}
8
- s.version = "0.2.9"
8
+ s.version = "0.4.5"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Padrino Team", "Nathan Esquenazi", "Davide D'Agostino", "Arthur Chiu"]
12
- s.date = %q{2009-12-22}
12
+ s.date = %q{2010-01-06}
13
13
  s.description = %q{Admin View for Padrino applications}
14
14
  s.email = %q{nesquena@gmail.com}
15
15
  s.extra_rdoc_files = [
@@ -23,11 +23,29 @@ Gem::Specification.new do |s|
23
23
  "Rakefile",
24
24
  "VERSION",
25
25
  "lib/padrino-admin.rb",
26
+ "lib/padrino-admin/access_control.rb",
27
+ "lib/padrino-admin/access_control/helpers.rb",
28
+ "lib/padrino-admin/adapters.rb",
29
+ "lib/padrino-admin/adapters/ar.rb",
30
+ "lib/padrino-admin/adapters/dm.rb",
31
+ "lib/padrino-admin/adapters/mm.rb",
26
32
  "lib/padrino-admin/ext_js/config.rb",
33
+ "lib/padrino-admin/ext_js/controller.rb",
27
34
  "lib/padrino-admin/generators/backend.rb",
35
+ "lib/padrino-admin/locale/en.yml",
36
+ "lib/padrino-admin/support.rb",
37
+ "lib/padrino-admin/utils/crypt.rb",
28
38
  "padrino-admin.gemspec",
39
+ "test/fixtures/active_record.rb",
40
+ "test/fixtures/data_mapper.rb",
41
+ "test/fixtures/mongo_mapper.rb",
29
42
  "test/helper.rb",
30
- "test/test_padrino_admin.rb",
43
+ "test/test_access_control.rb",
44
+ "test/test_active_record.rb",
45
+ "test/test_admin_application.rb",
46
+ "test/test_controller.rb",
47
+ "test/test_data_mapper.rb",
48
+ "test/test_mongo_mapper.rb",
31
49
  "test/test_parsing.rb"
32
50
  ]
33
51
  s.homepage = %q{http://github.com/padrino/padrino-framework/tree/master/padrino-admin}
@@ -41,16 +59,18 @@ Gem::Specification.new do |s|
41
59
  s.specification_version = 3
42
60
 
43
61
  if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
44
- s.add_runtime_dependency(%q<sinatra>, [">= 0.9.2"])
62
+ s.add_runtime_dependency(%q<json_pure>, [">= 1.2.0"])
45
63
  s.add_runtime_dependency(%q<padrino-core>, [">= 0.1.1"])
64
+ s.add_development_dependency(%q<dm-core>, [">= 0.10.2"])
46
65
  s.add_development_dependency(%q<haml>, [">= 2.2.1"])
47
66
  s.add_development_dependency(%q<shoulda>, [">= 0"])
48
67
  s.add_development_dependency(%q<mocha>, [">= 0.9.7"])
49
68
  s.add_development_dependency(%q<rack-test>, [">= 0.5.0"])
50
69
  s.add_development_dependency(%q<webrat>, [">= 0.5.1"])
51
70
  else
52
- s.add_dependency(%q<sinatra>, [">= 0.9.2"])
71
+ s.add_dependency(%q<json_pure>, [">= 1.2.0"])
53
72
  s.add_dependency(%q<padrino-core>, [">= 0.1.1"])
73
+ s.add_dependency(%q<dm-core>, [">= 0.10.2"])
54
74
  s.add_dependency(%q<haml>, [">= 2.2.1"])
55
75
  s.add_dependency(%q<shoulda>, [">= 0"])
56
76
  s.add_dependency(%q<mocha>, [">= 0.9.7"])
@@ -58,8 +78,9 @@ Gem::Specification.new do |s|
58
78
  s.add_dependency(%q<webrat>, [">= 0.5.1"])
59
79
  end
60
80
  else
61
- s.add_dependency(%q<sinatra>, [">= 0.9.2"])
81
+ s.add_dependency(%q<json_pure>, [">= 1.2.0"])
62
82
  s.add_dependency(%q<padrino-core>, [">= 0.1.1"])
83
+ s.add_dependency(%q<dm-core>, [">= 0.10.2"])
63
84
  s.add_dependency(%q<haml>, [">= 2.2.1"])
64
85
  s.add_dependency(%q<shoulda>, [">= 0"])
65
86
  s.add_dependency(%q<mocha>, [">= 0.9.7"])
@@ -0,0 +1,17 @@
1
+ require 'active_record'
2
+
3
+ ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
4
+
5
+ ActiveRecord::Schema.define do
6
+ create_table :accounts, :force => true do |t|
7
+ t.column :id, :integer
8
+ t.column :name, :string
9
+ t.column :role, :string
10
+ t.column :crypted_password, :string
11
+ t.column :salt, :string
12
+ t.column :email, :string
13
+ end
14
+ end
15
+
16
+ class Account < ActiveRecord::Base; end
17
+ Padrino::Admin::Adapters.register(:active_record)
@@ -0,0 +1,36 @@
1
+ require 'dm-core'
2
+ require 'dm-validations'
3
+
4
+ DataMapper.setup(:default, 'sqlite3::memory:')
5
+
6
+ # Fake Category Model
7
+ class Category
8
+ include DataMapper::Resource
9
+ property :id, Serial
10
+ property :name, String
11
+ belongs_to :account
12
+ end
13
+
14
+ # Fake Account Model
15
+ class Account
16
+ include DataMapper::Resource
17
+ property :id, Serial
18
+ property :name, String
19
+ has n, :categories
20
+ def self.admin; first(:role => "Admin"); end
21
+ def self.editor; first(:role => "Editor"); end
22
+ end
23
+
24
+ Padrino::Admin::Adapters.register(:data_mapper)
25
+ DataMapper.auto_migrate!
26
+
27
+ # We build some fake accounts
28
+ admin = Account.create(:name => "DAddYE", :role => "Admin", :email => "d.dagostino@lipsiasoft.com",
29
+ :password => "some", :password_confirmation => "some")
30
+ editor = Account.create(:name => "Dexter", :role => "Editor", :email => "editor@lipsiasoft.com",
31
+ :password => "some", :password_confirmation => "some")
32
+
33
+ %w(News Press HowTo).each do |c|
34
+ admin.categories.create(:name => c)
35
+ editor.categories.create(:name => c)
36
+ end
@@ -0,0 +1,12 @@
1
+ require 'mongo_mapper'
2
+
3
+ MongoMapper.connection = Mongo::Connection.new("127.0.0.1")
4
+ MongoMapper.database = 'test'
5
+
6
+ class Account
7
+ include MongoMapper::Document
8
+ key :name, String
9
+ end
10
+
11
+ Account.collection.remove
12
+ Padrino::Admin::Adapters.register(:mongo_mapper)
data/test/helper.rb CHANGED
@@ -1,58 +1,51 @@
1
+ ENV['PADRINO_ENV'] = 'test'
2
+ PADRINO_ROOT = File.dirname(__FILE__) unless defined? PADRINO_ROOT
3
+
1
4
  require 'rubygems'
2
5
  require 'test/unit'
3
- require 'shoulda'
4
- require 'mocha'
5
6
  require 'rack/test'
6
- require 'webrat'
7
-
8
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
9
- $LOAD_PATH.unshift(File.dirname(__FILE__))
10
-
11
- require 'padrino-gen'
7
+ require 'rack'
8
+ require 'shoulda'
12
9
  require 'padrino-admin'
13
10
 
14
- class Test::Unit::TestCase
15
- include Rack::Test::Methods
16
- include Webrat::Methods
17
- include Webrat::Matchers
18
-
19
- Webrat.configure do |config|
20
- config.mode = :rack
11
+ module Kernel
12
+ # Silences the output by redirecting to stringIO
13
+ # silence_logger { ...commands... } => "...output..."
14
+ def silence_logger(&block)
15
+ $stdout = $stderr = log_buffer = StringIO.new
16
+ block.call
17
+ $stdout = STDOUT
18
+ $stderr = STDERR
19
+ log_buffer.string
21
20
  end
21
+ alias :silence_stdout :silence_logger
22
22
 
23
- def stop_time_for_test
24
- time = Time.now
25
- Time.stubs(:now).returns(time)
26
- return time
23
+ def load_fixture(file)
24
+ Object.send(:remove_const, :Account) if defined?(Account)
25
+ file += ".rb" if file !~ /.rb$/
26
+ load File.join(File.dirname(__FILE__), "fixtures", file)
27
+ # silence_stdout { }
27
28
  end
29
+ end
28
30
 
29
- # assert_has_tag(:h1, :content => "yellow") { "<h1>yellow</h1>" }
30
- # In this case, block is the html to evaluate
31
- def assert_has_tag(name, attributes = {}, &block)
32
- html = block && block.call
33
- matcher = HaveSelector.new(name, attributes)
34
- raise "Please specify a block!" if html.blank?
35
- assert matcher.matches?(html), matcher.failure_message
36
- end
31
+ class Class
32
+ # Allow assertions in request context
33
+ include Test::Unit::Assertions
34
+ end
35
+
36
+ class Test::Unit::TestCase
37
+ include Rack::Test::Methods
37
38
 
38
- # assert_has_no_tag, tag(:h1, :content => "yellow") { "<h1>green</h1>" }
39
- # In this case, block is the html to evaluate
40
- def assert_has_no_tag(name, attributes = {}, &block)
41
- html = block && block.call
42
- attributes.merge!(:count => 0)
43
- matcher = HaveSelector.new(name, attributes)
44
- raise "Please specify a block!" if html.blank?
45
- assert matcher.matches?(html), matcher.failure_message
39
+ # Sets up a Sinatra::Base subclass defined with the block
40
+ # given. Used in setup or individual spec methods to establish
41
+ # the application.
42
+ def mock_app(base=Padrino::Application, &block)
43
+ base.use Rack::Session::Cookie # Need this because Sinatra 0.9.4 have use Rack::Session::Cookie if sessions? && !test?
44
+ @app = Sinatra.new(base, &block)
46
45
  end
47
46
 
48
- # Silences the output by redirecting to stringIO
49
- # silence_logger { ...commands... } => "...output..."
50
- def silence_logger(&block)
51
- orig_stdout = $stdout
52
- $stdout = log_buffer = StringIO.new
53
- block.call
54
- $stdout = orig_stdout
55
- log_buffer.rewind && log_buffer.read
47
+ def app
48
+ Rack::Lint.new(@app)
56
49
  end
57
50
 
58
51
  # Asserts that a file matches the pattern
@@ -60,12 +53,15 @@ class Test::Unit::TestCase
60
53
  assert File.exist?(file), "File '#{file}' does not exist!"
61
54
  assert_match pattern, File.read(file)
62
55
  end
63
- end
64
56
 
65
- module Webrat
66
- module Logging
67
- def logger # :nodoc:
68
- @logger = nil
57
+ # Delegate other missing methods to response.
58
+ def method_missing(name, *args, &block)
59
+ if response && response.respond_to?(name)
60
+ response.send(name, *args, &block)
61
+ else
62
+ super
69
63
  end
70
64
  end
65
+
66
+ alias :response :last_response
71
67
  end