puffer 0.0.1
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/MIT-LICENSE +20 -0
- data/README.rdoc +3 -0
- data/Rakefile +36 -0
- data/lib/generators/puffer/controller/USAGE +18 -0
- data/lib/generators/puffer/controller/controller_generator.rb +21 -0
- data/lib/generators/puffer/controller/templates/controller.rb +16 -0
- data/lib/generators/puffer/install/USAGE +6 -0
- data/lib/generators/puffer/install/install_generator.rb +3 -0
- data/lib/puffer/base.rb +46 -0
- data/lib/puffer/controller/actions.rb +79 -0
- data/lib/puffer/controller/config.rb +28 -0
- data/lib/puffer/controller/dsl.rb +67 -0
- data/lib/puffer/controller/helpers.rb +34 -0
- data/lib/puffer/controller/mutate.rb +48 -0
- data/lib/puffer/extensions/activerecord.rb +23 -0
- data/lib/puffer/extensions/controller.rb +25 -0
- data/lib/puffer/extensions/core.rb +36 -0
- data/lib/puffer/extensions/mapper.rb +125 -0
- data/lib/puffer/field.rb +88 -0
- data/lib/puffer/railtie.rb +5 -0
- data/lib/puffer/resource/routing.rb +42 -0
- data/lib/puffer/resource/scoping.rb +9 -0
- data/lib/puffer/resource.rb +140 -0
- data/lib/puffer.rb +8 -0
- metadata +90 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2010 puffer
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'rubygems'
|
3
|
+
begin
|
4
|
+
require 'bundler/setup'
|
5
|
+
rescue LoadError
|
6
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'rake'
|
10
|
+
require 'rake/rdoctask'
|
11
|
+
|
12
|
+
require 'rspec/core'
|
13
|
+
require 'rspec/core/rake_task'
|
14
|
+
|
15
|
+
RSpec::Core::RakeTask.new(:spec)
|
16
|
+
|
17
|
+
task :default => :spec
|
18
|
+
|
19
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
20
|
+
rdoc.rdoc_dir = 'rdoc'
|
21
|
+
rdoc.title = 'Puffer'
|
22
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
23
|
+
rdoc.rdoc_files.include('README.rdoc')
|
24
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
25
|
+
end
|
26
|
+
|
27
|
+
require 'jeweler'
|
28
|
+
|
29
|
+
Jeweler::Tasks.new do |gem|
|
30
|
+
gem.name = "puffer"
|
31
|
+
gem.summary = %Q{Admin interface generator}
|
32
|
+
gem.description = %Q{Puffer}
|
33
|
+
gem.email = "kinwizard@gmail.com"
|
34
|
+
gem.homepage = "http://github.com/puffer/puffer"
|
35
|
+
gem.authors = ["pyromaniac"]
|
36
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
Description:
|
2
|
+
Create puffer controller for specified model
|
3
|
+
|
4
|
+
Example:
|
5
|
+
rails generate puffer:controller User
|
6
|
+
rails generate puffer:controller user
|
7
|
+
rails generate puffer:controller users
|
8
|
+
|
9
|
+
This will create:
|
10
|
+
app/controllers/admin/users_controller.rb
|
11
|
+
|
12
|
+
rails generate puffer:controller Moderator::User
|
13
|
+
rails generate puffer:controller moderator/user
|
14
|
+
rails generate puffer:controller moderator/users
|
15
|
+
|
16
|
+
This will create:
|
17
|
+
app/controllers/moderator/users_controller.rb
|
18
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class Puffer::ControllerGenerator < Rails::Generators::NamedBase
|
2
|
+
source_root File.expand_path('../templates', __FILE__)
|
3
|
+
|
4
|
+
def generate_controller
|
5
|
+
@modules = name.classify.split('::')
|
6
|
+
@model_name = @modules.delete_at(-1)
|
7
|
+
|
8
|
+
template('controller.rb', "app/controllers/#{controller_name.underscore}_controller.rb")
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def controller_name
|
14
|
+
((@modules.presence || ['Admin']) << @model_name.pluralize).join('::')
|
15
|
+
end
|
16
|
+
|
17
|
+
def attributes
|
18
|
+
@model_name.constantize.columns.map(&:name)
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class <%= controller_name %>Controller < Puffer::Base
|
2
|
+
before_filter :i_didnt_forget_to_protect_this
|
3
|
+
|
4
|
+
index do
|
5
|
+
<% attributes.each do |attribute| -%>
|
6
|
+
field :<%= attribute %>
|
7
|
+
<% end -%>
|
8
|
+
end
|
9
|
+
|
10
|
+
form do
|
11
|
+
<% attributes.each do |attribute| -%>
|
12
|
+
field :<%= attribute %>
|
13
|
+
<% end -%>
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
data/lib/puffer/base.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
module Puffer
|
2
|
+
class Base < ApplicationController
|
3
|
+
unloadable
|
4
|
+
|
5
|
+
respond_to :html
|
6
|
+
|
7
|
+
include Puffer::Controller::Mutate
|
8
|
+
include Puffer::Controller::Dsl
|
9
|
+
include Puffer::Controller::Helpers
|
10
|
+
|
11
|
+
def index
|
12
|
+
@records = current_resource.collection
|
13
|
+
end
|
14
|
+
|
15
|
+
def show
|
16
|
+
@record = current_resource.member
|
17
|
+
end
|
18
|
+
|
19
|
+
def new
|
20
|
+
@record = current_resource.new_member
|
21
|
+
end
|
22
|
+
|
23
|
+
def edit
|
24
|
+
@record = current_resource.member
|
25
|
+
end
|
26
|
+
|
27
|
+
def create
|
28
|
+
@record = current_resource.new_member
|
29
|
+
@record.save
|
30
|
+
respond_with @record, :location => current_resource.path
|
31
|
+
end
|
32
|
+
|
33
|
+
def update
|
34
|
+
@record = current_resource.member
|
35
|
+
@record.update_attributes current_resource.attributes
|
36
|
+
respond_with @record, :location => current_resource.path
|
37
|
+
end
|
38
|
+
|
39
|
+
def destroy
|
40
|
+
@record = current_resource.member
|
41
|
+
@record.destroy
|
42
|
+
redirect_to current_resource.path
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module Puffer
|
2
|
+
module Controller
|
3
|
+
module Actions
|
4
|
+
|
5
|
+
def self.included base
|
6
|
+
base.class_eval do
|
7
|
+
extend ClassMethods
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
|
13
|
+
def generate_association_actions field
|
14
|
+
field.collection? ? generate_collection_association_actions(field) : generate_single_association_actions(field)
|
15
|
+
end
|
16
|
+
|
17
|
+
def generate_single_association_actions field
|
18
|
+
define_method "associated_#{field}_choosing" do
|
19
|
+
@record = model.find params[:id]
|
20
|
+
@records = field.association.klass.scoped(:conditions => search_query(field.association_fields)).paginate(:page => params[:page], :include => includes(field.association_fields))
|
21
|
+
@field = field
|
22
|
+
render 'puffer/associated/one'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def generate_collection_association_actions field
|
27
|
+
define_method "associated_#{field}" do
|
28
|
+
@record = model.find params[:id]
|
29
|
+
@records = field.association.klass.scoped(:conditions => {:id => params[:ids]}).scoped(:conditions => search_query(field.association_fields)).paginate(:page => params[:page], :include => includes(field.association_fields))
|
30
|
+
@field = field
|
31
|
+
render 'puffer/associated/many'
|
32
|
+
end
|
33
|
+
|
34
|
+
define_method "associated_#{field}_choosing" do
|
35
|
+
@record = model.find params[:id]
|
36
|
+
@records = field.association.klass.scoped(:conditions => search_query(field.association_fields)).paginate(:page => params[:page], :include => includes(field.association_fields))
|
37
|
+
@choosen = field.association.klass.scoped(:conditions => {:id => params[:ids]}).scoped(:conditions => search_query(field.association_fields)).paginate(:page => params[:page], :include => includes(field.association_fields))
|
38
|
+
@field = field
|
39
|
+
render 'puffer/associated/many'
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def generate_change_actions field
|
44
|
+
define_method "toggle_#{field}" do
|
45
|
+
@record = model.find params[:id]
|
46
|
+
@field = field
|
47
|
+
@record.toggle! field.name.to_sym
|
48
|
+
render 'puffer/toggle'
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def route_member_actions
|
55
|
+
unless @route_member_actions
|
56
|
+
@route_member_actions = {}
|
57
|
+
actions.each do |action|
|
58
|
+
@route_member_actions.merge!(action => :get)
|
59
|
+
end
|
60
|
+
[:form_fields, :update_fields, :create_fields].each do |fields|
|
61
|
+
fields = send fields
|
62
|
+
fields.each do |field|
|
63
|
+
if field.association?
|
64
|
+
field.collection? ? @route_member_actions.merge!("associated_#{field}" => :get, "associated_#{field}_choosing" => :get) : @route_member_actions.merge!("associated_#{field}_choosing" => :get)
|
65
|
+
end
|
66
|
+
end if fields
|
67
|
+
end
|
68
|
+
index_fields.each do |field|
|
69
|
+
@route_member_actions.merge!("toggle_#{field}" => :post) if field.toggable?
|
70
|
+
end if index_fields
|
71
|
+
end
|
72
|
+
@route_member_actions
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Puffer
|
2
|
+
module Controller
|
3
|
+
class Config
|
4
|
+
|
5
|
+
attr_accessor :config
|
6
|
+
cattr_accessor :default_config
|
7
|
+
@@default_config = {}
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@config = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.option name, default
|
14
|
+
@@default_config[name.to_sym] = default
|
15
|
+
class_eval <<-EOS
|
16
|
+
def #{name} value = nil
|
17
|
+
value.nil? ? (@config.key?(:#{name}) ? @config[:#{name}] : self.class.default_config[:#{name}]) : @config[:#{name}] = value
|
18
|
+
end
|
19
|
+
EOS
|
20
|
+
end
|
21
|
+
|
22
|
+
option :destroy, true
|
23
|
+
option :model, nil
|
24
|
+
option :scope, {}
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Puffer
|
2
|
+
module Controller
|
3
|
+
module Dsl
|
4
|
+
|
5
|
+
def self.included base
|
6
|
+
base.class_eval do
|
7
|
+
class_attribute :puffer_fields
|
8
|
+
self.puffer_fields = {}
|
9
|
+
|
10
|
+
extend ClassMethods
|
11
|
+
|
12
|
+
helper_method :index_fields, :show_fields, :form_fields, :create_fields, :update_fields
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
[:index, :show, :form, :create, :update].each do |sym|
|
17
|
+
define_method "#{sym}_fields" do
|
18
|
+
self.class.send "#{sym}_fields"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
module ClassMethods
|
23
|
+
|
24
|
+
def configure &block
|
25
|
+
block.bind(current_config).call
|
26
|
+
end
|
27
|
+
|
28
|
+
[:index, :show, :form, :create, :update].each do |sym|
|
29
|
+
define_method sym do
|
30
|
+
@puffer_option = sym
|
31
|
+
yield if block_given?
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def index_fields
|
36
|
+
puffer_fields[:index] || []
|
37
|
+
end
|
38
|
+
|
39
|
+
def show_fields
|
40
|
+
puffer_fields[:show] || puffer_fields[:index] || []
|
41
|
+
end
|
42
|
+
|
43
|
+
def form_fields
|
44
|
+
puffer_fields[:form] || []
|
45
|
+
end
|
46
|
+
|
47
|
+
def create_fields
|
48
|
+
puffer_fields[:create] || puffer_fields[:form] || []
|
49
|
+
end
|
50
|
+
|
51
|
+
def update_fields
|
52
|
+
puffer_fields[:update] || puffer_fields[:form] || []
|
53
|
+
end
|
54
|
+
|
55
|
+
def field name, options = {}
|
56
|
+
puffer_fields[@puffer_option] ||= []
|
57
|
+
field = ::Puffer::Field.new(current_resource.model, name, options)
|
58
|
+
generate_association_actions field if field.association?
|
59
|
+
generate_change_actions field if field.toggable?
|
60
|
+
puffer_fields[@puffer_option] << field
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Puffer
|
2
|
+
module Controller
|
3
|
+
module Helpers
|
4
|
+
|
5
|
+
def self.included base
|
6
|
+
base.class_eval do
|
7
|
+
helper_method :resource_session, :searchable_fields, :boolean_fields, :puffer_navigation
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def puffer_navigation
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
def resource_session
|
16
|
+
postfix = params[:action] =~ /associated_/ ? params[:action] : ''
|
17
|
+
name = "#{current_resource.model_name}#{postfix}".to_sym
|
18
|
+
session[:resources] ||= {}
|
19
|
+
session[:resources][name] ||= {}
|
20
|
+
session[:resources][name][:boolean] ||= {}
|
21
|
+
session[:resources][name]
|
22
|
+
end
|
23
|
+
|
24
|
+
def searchable_fields fields
|
25
|
+
@searchable_fields ||= fields.map { |f| f if [:text, :string, :integer, :decimal, :float].include? f.type }.compact
|
26
|
+
end
|
27
|
+
|
28
|
+
def boolean_fields
|
29
|
+
@boolean_fields ||= index_fields.map { |f| f if ['boolean'].include? f.type.to_s }.compact
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Puffer
|
2
|
+
module Controller
|
3
|
+
module Mutate
|
4
|
+
|
5
|
+
def self.included base
|
6
|
+
base.class_eval do
|
7
|
+
class_attribute :current_config
|
8
|
+
self.current_config = Puffer::Controller::Config.new
|
9
|
+
|
10
|
+
extend ClassMethods
|
11
|
+
|
12
|
+
layout 'puffer'
|
13
|
+
|
14
|
+
helper_method :current_resource, :current_config, :record, :records
|
15
|
+
|
16
|
+
rescue_from ActionView::MissingTemplate do |exception|
|
17
|
+
render current_resource.template(exception.path.split('/').last)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def current_resource
|
23
|
+
@current_resource ||= Puffer::Resource.new params, request
|
24
|
+
end
|
25
|
+
|
26
|
+
def record
|
27
|
+
@record || instance_variable_get("@#{current_resource.model_name}")
|
28
|
+
end
|
29
|
+
|
30
|
+
def records
|
31
|
+
@records || instance_variable_get("@#{current_resource.model_name.pluralize}")
|
32
|
+
end
|
33
|
+
|
34
|
+
module ClassMethods
|
35
|
+
|
36
|
+
def puffer?
|
37
|
+
true
|
38
|
+
end
|
39
|
+
|
40
|
+
def configure &block
|
41
|
+
block.bind(current_config).call
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Puffer
|
2
|
+
module Extensions
|
3
|
+
module ActiveRecord
|
4
|
+
module Base
|
5
|
+
|
6
|
+
def call_chain chain
|
7
|
+
swallow_nil{instance_eval(chain.to_s)}
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_title
|
11
|
+
send title_column
|
12
|
+
end
|
13
|
+
|
14
|
+
def title_column
|
15
|
+
self.class.column_names[1].to_sym
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
ActiveRecord::Base.send :include, Puffer::Extensions::ActiveRecord::Base
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Puffer
|
2
|
+
module Extensions
|
3
|
+
module ActionController
|
4
|
+
module Base
|
5
|
+
|
6
|
+
def self.included base
|
7
|
+
base.class_eval do
|
8
|
+
extend ClassMethods
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
|
14
|
+
def puffer?
|
15
|
+
false
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
ActionController::Base.send :include, Puffer::Extensions::ActionController::Base
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Puffer
|
2
|
+
module Extensions
|
3
|
+
|
4
|
+
module String
|
5
|
+
def singular?
|
6
|
+
self.singularize == self
|
7
|
+
end
|
8
|
+
|
9
|
+
def plural?
|
10
|
+
self.pluralize == self
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
module Symbol
|
15
|
+
def singular?
|
16
|
+
to_s.singular?
|
17
|
+
end
|
18
|
+
|
19
|
+
def plural?
|
20
|
+
to_s.plural?
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
String.send :include, Puffer::Extensions::String
|
28
|
+
Symbol.send :include, Puffer::Extensions::Symbol
|
29
|
+
|
30
|
+
Kernel.class_eval do
|
31
|
+
def swallow_nil
|
32
|
+
yield
|
33
|
+
rescue NoMethodError
|
34
|
+
nil
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
module Puffer
|
2
|
+
module Extensions
|
3
|
+
module Mapper
|
4
|
+
|
5
|
+
def self.included base
|
6
|
+
base.class_eval do
|
7
|
+
alias_method :original_resource, :resource
|
8
|
+
alias_method :original_resources, :resources
|
9
|
+
|
10
|
+
include InstanceMethods
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
module InstanceMethods
|
15
|
+
|
16
|
+
def resource *resources, &block
|
17
|
+
puffer_resource(*resources, &block) || original_resource(*resources, &block)
|
18
|
+
end
|
19
|
+
|
20
|
+
def resources *resources, &block
|
21
|
+
puffer_resources(*resources, &block) || original_resources(*resources, &block)
|
22
|
+
end
|
23
|
+
|
24
|
+
def puffer_resource(*resources, &block)
|
25
|
+
options = resources.extract_options!
|
26
|
+
|
27
|
+
if apply_common_behavior_for(:resource, resources, options, &block)
|
28
|
+
return self
|
29
|
+
end
|
30
|
+
|
31
|
+
resource = ActionDispatch::Routing::Mapper::Resources::SingletonResource.new(resources.pop, options)
|
32
|
+
controller = "#{[@scope[:module], resource.controller].compact.join("/")}_controller".classify.constantize rescue nil
|
33
|
+
|
34
|
+
return if controller.nil? || (controller && !controller.puffer?)
|
35
|
+
|
36
|
+
@scope[:ancestors] ||= []
|
37
|
+
@scope[:children] ||= []
|
38
|
+
|
39
|
+
resource_scope(resource) do
|
40
|
+
siblings = @scope[:children].dup
|
41
|
+
@scope[:children] = []
|
42
|
+
@scope[:ancestors].push resource.singular.to_sym
|
43
|
+
|
44
|
+
yield if block_given?
|
45
|
+
|
46
|
+
@scope[:ancestors].pop
|
47
|
+
options = {:plural => false, :ancestors => @scope[:ancestors].dup, :children => @scope[:children].dup}
|
48
|
+
siblings.push resource.singular.to_sym
|
49
|
+
@scope[:children] = siblings
|
50
|
+
|
51
|
+
collection do
|
52
|
+
post :create, options
|
53
|
+
end
|
54
|
+
|
55
|
+
new do
|
56
|
+
get :new, options
|
57
|
+
end
|
58
|
+
|
59
|
+
member do
|
60
|
+
get :edit, options
|
61
|
+
get :show, options
|
62
|
+
put :update, options
|
63
|
+
delete :destroy, options
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
self
|
69
|
+
end
|
70
|
+
|
71
|
+
def puffer_resources(*resources, &block)
|
72
|
+
options = resources.extract_options!
|
73
|
+
|
74
|
+
if apply_common_behavior_for(:resources, resources, options, &block)
|
75
|
+
return self
|
76
|
+
end
|
77
|
+
|
78
|
+
resource = ActionDispatch::Routing::Mapper::Resources::Resource.new(resources.pop, options)
|
79
|
+
controller = "#{[@scope[:module], resource.controller].compact.join("/")}_controller".classify.constantize rescue nil
|
80
|
+
|
81
|
+
return if controller.nil? || (controller && !controller.puffer?)
|
82
|
+
|
83
|
+
@scope[:ancestors] ||= []
|
84
|
+
@scope[:children] ||= []
|
85
|
+
|
86
|
+
resource_scope(resource) do
|
87
|
+
siblings = @scope[:children].dup
|
88
|
+
@scope[:children] = []
|
89
|
+
@scope[:ancestors].push resource.plural.to_sym
|
90
|
+
|
91
|
+
yield if block_given?
|
92
|
+
|
93
|
+
@scope[:ancestors].pop
|
94
|
+
options = {:plural => true, :ancestors => @scope[:ancestors].dup, :children => @scope[:children].dup}
|
95
|
+
siblings.push resource.plural.to_sym
|
96
|
+
@scope[:children] = siblings
|
97
|
+
|
98
|
+
|
99
|
+
collection do
|
100
|
+
get :index, options
|
101
|
+
post :create, options
|
102
|
+
end
|
103
|
+
|
104
|
+
new do
|
105
|
+
get :new, options
|
106
|
+
end
|
107
|
+
|
108
|
+
member do
|
109
|
+
get :edit, options
|
110
|
+
get :show, options
|
111
|
+
put :update, options
|
112
|
+
delete :destroy, options
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
self
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
ActionDispatch::Routing::Mapper.send :include, Puffer::Extensions::Mapper
|
data/lib/puffer/field.rb
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
module Puffer
|
2
|
+
class Field
|
3
|
+
|
4
|
+
attr_accessor :name, :options, :main_model
|
5
|
+
|
6
|
+
def initialize(model, name, options = {})
|
7
|
+
@main_model = model
|
8
|
+
@name = name
|
9
|
+
@options = options
|
10
|
+
end
|
11
|
+
|
12
|
+
def order
|
13
|
+
@order ||= options[:order] || query_column
|
14
|
+
end
|
15
|
+
|
16
|
+
def label
|
17
|
+
@label ||= options[:label] || @name.to_s.humanize
|
18
|
+
end
|
19
|
+
|
20
|
+
def [](key)
|
21
|
+
@options[key]
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_s
|
25
|
+
@name.to_s
|
26
|
+
end
|
27
|
+
|
28
|
+
def own?
|
29
|
+
model == main_model
|
30
|
+
end
|
31
|
+
|
32
|
+
def toggable?
|
33
|
+
options[:toggable] = true if options[:toggable].nil?
|
34
|
+
own? && type == :boolean && options[:toggable]
|
35
|
+
end
|
36
|
+
|
37
|
+
def association
|
38
|
+
@association ||= main_model.reflect_on_association(to_s.split('.').first.to_sym)
|
39
|
+
end
|
40
|
+
|
41
|
+
def association?
|
42
|
+
!!association
|
43
|
+
end
|
44
|
+
|
45
|
+
def collection?
|
46
|
+
association? && [:has_many, :has_and_belongs_to_many].include?(association.macro)
|
47
|
+
end
|
48
|
+
|
49
|
+
def association_fields
|
50
|
+
@association_fields ||= @options[:fields].map {|sym| self.class.new(association.klass, sym) }
|
51
|
+
end
|
52
|
+
|
53
|
+
def association_key
|
54
|
+
association.primary_key_name
|
55
|
+
end
|
56
|
+
|
57
|
+
def record
|
58
|
+
name.split('.')[0..-2].join('.')
|
59
|
+
end
|
60
|
+
|
61
|
+
def model
|
62
|
+
unless @model
|
63
|
+
try_model = to_s.split('.')[-2]
|
64
|
+
@model = try_model.classify.constantize rescue nil if try_model
|
65
|
+
@model ||= main_model
|
66
|
+
end
|
67
|
+
@model
|
68
|
+
end
|
69
|
+
|
70
|
+
def type
|
71
|
+
@options[:type] = :association if association?
|
72
|
+
@options[:type].to_sym || swallow_nil{column.type}
|
73
|
+
end
|
74
|
+
|
75
|
+
def column
|
76
|
+
@column ||= model.columns.detect { |c| c.name == to_s.split('.').last}
|
77
|
+
end
|
78
|
+
|
79
|
+
def column?
|
80
|
+
!!column
|
81
|
+
end
|
82
|
+
|
83
|
+
def query_column
|
84
|
+
"#{model.to_s.tableize}.#{to_s.split('.').last}" if column
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Puffer
|
2
|
+
class Resource
|
3
|
+
module Routing
|
4
|
+
|
5
|
+
include ActionController::UrlFor
|
6
|
+
include Rails.application.routes.url_helpers
|
7
|
+
|
8
|
+
def index_url *args
|
9
|
+
polymorphic_url *route_args(route_member(controller_name), *args)
|
10
|
+
end
|
11
|
+
|
12
|
+
def url *args
|
13
|
+
suggest = args.shift if args.first.is_a? ActiveRecord::Base
|
14
|
+
polymorphic_url *route_args(route_member(suggest), *args)
|
15
|
+
end
|
16
|
+
|
17
|
+
def new_url *args
|
18
|
+
new_polymorphic_url *route_args(controller_name.singularize, *args)
|
19
|
+
end
|
20
|
+
|
21
|
+
def edit_url *args
|
22
|
+
suggest = args.shift if args.first.is_a? ActiveRecord::Base
|
23
|
+
edit_polymorphic_url *route_args(route_member(suggest), *args)
|
24
|
+
end
|
25
|
+
|
26
|
+
def route_args *args
|
27
|
+
options = args.extract_options!
|
28
|
+
resource = args.shift
|
29
|
+
return args + [prefix] + ancestors.map(&:route_member) + [resource], options
|
30
|
+
end
|
31
|
+
|
32
|
+
def route_member suggest = nil
|
33
|
+
plural? ? (suggest || member) : controller_name.singularize
|
34
|
+
end
|
35
|
+
|
36
|
+
def default_url_options *args
|
37
|
+
Puffer::Base.default_url_options *args
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
module Puffer
|
2
|
+
|
3
|
+
##############################################################
|
4
|
+
#
|
5
|
+
# Resource is presenter layer for controllers.
|
6
|
+
#
|
7
|
+
##############################################################
|
8
|
+
|
9
|
+
class Resource
|
10
|
+
|
11
|
+
include Routing
|
12
|
+
include Scoping
|
13
|
+
|
14
|
+
attr_reader :request, :params, :prefix, :action, :controller_name, :model_name, :controller, :model
|
15
|
+
|
16
|
+
def initialize params, request = nil
|
17
|
+
@action = params.delete :action
|
18
|
+
@controller = "#{params[:controller]}_controller".classify.constantize
|
19
|
+
controller_segments = params.delete(:controller).split('/')
|
20
|
+
@prefix = controller_segments.first
|
21
|
+
@controller_name = controller_segments.last
|
22
|
+
@model_name = (controller.current_config.model || controller_name.singularize).to_s
|
23
|
+
@model = model_name.classify.constantize
|
24
|
+
@params = params
|
25
|
+
@request = request
|
26
|
+
end
|
27
|
+
|
28
|
+
def plural?
|
29
|
+
params[:plural]
|
30
|
+
end
|
31
|
+
|
32
|
+
def human_name
|
33
|
+
model_name.humanize
|
34
|
+
end
|
35
|
+
|
36
|
+
def parent
|
37
|
+
@parent ||= begin
|
38
|
+
parent_ancestors = params[:ancestors].dup
|
39
|
+
parent_name = parent_ancestors.pop
|
40
|
+
if parent_name
|
41
|
+
parent_params = ActiveSupport::HashWithIndifferentAccess.new({
|
42
|
+
:controller => "#{prefix}/#{parent_name.to_s.pluralize}",
|
43
|
+
:action => 'index',
|
44
|
+
:plural => parent_name.plural?,
|
45
|
+
:ancestors => parent_ancestors,
|
46
|
+
:children => []
|
47
|
+
})
|
48
|
+
|
49
|
+
parent_ancestors.each do |ancestor|
|
50
|
+
key = ancestor.to_s.singularize.foreign_key
|
51
|
+
parent_params.merge! key => params[key] if params[key]
|
52
|
+
end
|
53
|
+
parent_params.merge! :id => params[parent_name.to_s.singularize.foreign_key]
|
54
|
+
|
55
|
+
self.class.new parent_params, request
|
56
|
+
else
|
57
|
+
nil
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def ancestors
|
63
|
+
@ancestors ||= begin
|
64
|
+
ancestors = []
|
65
|
+
resource = self
|
66
|
+
while resource = resource.parent do
|
67
|
+
ancestors.unshift resource
|
68
|
+
end
|
69
|
+
ancestors
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def children(custom_params = {})
|
74
|
+
@children ||= params[:children].map do |child_name|
|
75
|
+
child_params = ActiveSupport::HashWithIndifferentAccess.new(custom_params.deep_merge({
|
76
|
+
:controller => "#{prefix}/#{child_name.to_s.pluralize}",
|
77
|
+
:action => 'index',
|
78
|
+
:plural => child_name.plural?,
|
79
|
+
:ancestors => params[:ancestors].dup.push((plural? ? controller_name : controller_name.singularize).to_sym),
|
80
|
+
:children => []
|
81
|
+
}))
|
82
|
+
|
83
|
+
params[:ancestors].each do |ancestor|
|
84
|
+
key = ancestor.to_s.singularize.foreign_key
|
85
|
+
child_params.merge! key => params[key] if params[key]
|
86
|
+
end
|
87
|
+
child_params.merge! controller_name.singularize.foreign_key => params[:id] if params[:id]
|
88
|
+
|
89
|
+
self.class.new child_params, request
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def collection
|
94
|
+
scope = parent ? parent.member.send(model_name.pluralize) : model
|
95
|
+
scope.paginate :page => params[:page]
|
96
|
+
end
|
97
|
+
|
98
|
+
def member
|
99
|
+
if parent
|
100
|
+
if plural?
|
101
|
+
parent.member.send(model_name.pluralize).find params[:id]
|
102
|
+
else
|
103
|
+
parent.member.send(model_name)
|
104
|
+
end
|
105
|
+
else
|
106
|
+
model.find params[:id]
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def new_member
|
111
|
+
if parent
|
112
|
+
if plural?
|
113
|
+
parent.member.send(model_name.pluralize).new attributes
|
114
|
+
else
|
115
|
+
parent.member.send("build_#{model_name}")
|
116
|
+
end
|
117
|
+
else
|
118
|
+
model.new attributes
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def attributes
|
123
|
+
params[model_name] || {}
|
124
|
+
end
|
125
|
+
|
126
|
+
def template suggest = nil
|
127
|
+
"puffer/#{suggest || action}"
|
128
|
+
end
|
129
|
+
|
130
|
+
def method_missing method, *args, &block
|
131
|
+
method = method.to_s
|
132
|
+
if method.match(/path$/)
|
133
|
+
options = args.extract_options!
|
134
|
+
return send method.gsub(/path$/, 'url'), *(args << options.merge(:routing_type => :path))
|
135
|
+
end
|
136
|
+
model.send method, *args, &block
|
137
|
+
end
|
138
|
+
|
139
|
+
end
|
140
|
+
end
|
data/lib/puffer.rb
ADDED
metadata
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: puffer
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors: []
|
13
|
+
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-12-30 00:00:00 +03:00
|
19
|
+
default_executable:
|
20
|
+
dependencies: []
|
21
|
+
|
22
|
+
description: Insert Puffer description.
|
23
|
+
email:
|
24
|
+
executables: []
|
25
|
+
|
26
|
+
extensions: []
|
27
|
+
|
28
|
+
extra_rdoc_files: []
|
29
|
+
|
30
|
+
files:
|
31
|
+
- lib/puffer.rb
|
32
|
+
- lib/generators/puffer/controller/USAGE
|
33
|
+
- lib/generators/puffer/controller/controller_generator.rb
|
34
|
+
- lib/generators/puffer/controller/templates/controller.rb
|
35
|
+
- lib/generators/puffer/install/USAGE
|
36
|
+
- lib/generators/puffer/install/install_generator.rb
|
37
|
+
- lib/puffer/controller/config.rb
|
38
|
+
- lib/puffer/controller/helpers.rb
|
39
|
+
- lib/puffer/controller/mutate.rb
|
40
|
+
- lib/puffer/controller/dsl.rb
|
41
|
+
- lib/puffer/controller/actions.rb
|
42
|
+
- lib/puffer/field.rb
|
43
|
+
- lib/puffer/base.rb
|
44
|
+
- lib/puffer/extensions/mapper.rb
|
45
|
+
- lib/puffer/extensions/activerecord.rb
|
46
|
+
- lib/puffer/extensions/controller.rb
|
47
|
+
- lib/puffer/extensions/core.rb
|
48
|
+
- lib/puffer/resource.rb
|
49
|
+
- lib/puffer/railtie.rb
|
50
|
+
- lib/puffer/resource/scoping.rb
|
51
|
+
- lib/puffer/resource/routing.rb
|
52
|
+
- MIT-LICENSE
|
53
|
+
- Rakefile
|
54
|
+
- README.rdoc
|
55
|
+
has_rdoc: true
|
56
|
+
homepage:
|
57
|
+
licenses: []
|
58
|
+
|
59
|
+
post_install_message:
|
60
|
+
rdoc_options: []
|
61
|
+
|
62
|
+
require_paths:
|
63
|
+
- lib
|
64
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
hash: 3
|
70
|
+
segments:
|
71
|
+
- 0
|
72
|
+
version: "0"
|
73
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
hash: 3
|
79
|
+
segments:
|
80
|
+
- 0
|
81
|
+
version: "0"
|
82
|
+
requirements: []
|
83
|
+
|
84
|
+
rubyforge_project:
|
85
|
+
rubygems_version: 1.3.7
|
86
|
+
signing_key:
|
87
|
+
specification_version: 3
|
88
|
+
summary: Insert Puffer summary.
|
89
|
+
test_files: []
|
90
|
+
|