padrino-admin 0.2.9 → 0.4.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -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