puffer 0.0.29 → 0.0.30

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. data/Gemfile +1 -0
  2. data/Gemfile.lock +56 -52
  3. data/README.md +51 -43
  4. data/VERSION +1 -1
  5. data/app/assets/javascripts/puffer/application.js +3 -1
  6. data/app/assets/stylesheets/puffer/application.css +2 -0
  7. data/app/assets/stylesheets/puffer/puffer.css +5 -9
  8. data/app/components/base_component.rb +0 -1
  9. data/app/components/boolean/index.html.erb +1 -1
  10. data/app/components/boolean_component.rb +1 -1
  11. data/app/components/references_many/index.html.erb +1 -0
  12. data/app/components/references_many_component.rb +8 -0
  13. data/app/components/references_one_component.rb +6 -1
  14. data/app/components/select/filter.html.erb +2 -0
  15. data/app/components/select/form.html.erb +1 -1
  16. data/app/components/select_component.rb +13 -2
  17. data/app/controllers/admin/puffer_users_controller.rb +5 -1
  18. data/app/controllers/admin/sessions_controller.rb +4 -1
  19. data/app/controllers/puffer/base.rb +1 -1
  20. data/app/controllers/puffer/sessions_base.rb +3 -0
  21. data/app/controllers/puffer/tree_base.rb +4 -4
  22. data/app/helpers/puffer_helper.rb +16 -15
  23. data/app/models/puffer/puffer_user.rb +11 -10
  24. data/app/models/puffer_user.rb +14 -2
  25. data/app/views/layouts/puffer.html.erb +16 -17
  26. data/app/views/layouts/puffer_base.html.erb +1 -2
  27. data/app/views/layouts/puffer_dashboard.html.erb +2 -2
  28. data/app/views/puffer/base/edit.html.erb +12 -3
  29. data/app/views/puffer/base/new.html.erb +12 -3
  30. data/app/views/puffer/dashboard_base/index.html.erb +5 -5
  31. data/lib/generators/puffer/component/component_generator.rb +0 -1
  32. data/lib/generators/puffer/controller/controller_generator.rb +1 -1
  33. data/lib/generators/puffer/controller/templates/controller.rb +8 -0
  34. data/lib/puffer.rb +77 -13
  35. data/lib/puffer/component.rb +14 -9
  36. data/lib/puffer/controller/auth.rb +41 -2
  37. data/lib/puffer/controller/config.rb +1 -1
  38. data/lib/puffer/controller/mutate.rb +15 -6
  39. data/lib/puffer/extensions/core.rb +4 -0
  40. data/lib/puffer/extensions/directive_processor.rb +36 -0
  41. data/lib/puffer/extensions/form.rb +1 -1
  42. data/lib/puffer/extensions/mapper.rb +74 -149
  43. data/lib/puffer/field.rb +2 -4
  44. data/lib/puffer/field_set.rb +3 -15
  45. data/lib/puffer/filters.rb +105 -0
  46. data/lib/puffer/orm_adapter/active_record.rb +33 -0
  47. data/lib/puffer/orm_adapter/base.rb +4 -0
  48. data/lib/puffer/orm_adapter/mongoid.rb +29 -0
  49. data/lib/puffer/resource.rb +63 -62
  50. data/lib/puffer/resource/node.rb +71 -0
  51. data/lib/puffer/resource/routing.rb +4 -4
  52. data/lib/puffer/resource/tree.rb +36 -0
  53. data/puffer.gemspec +28 -8
  54. data/spec/dummy/app/controllers/admin/posts_controller.rb +1 -1
  55. data/spec/dummy/app/controllers/admin/users_controller.rb +1 -0
  56. data/spec/dummy/app/controllers/application_controller.rb +4 -0
  57. data/spec/dummy/app/controllers/orms/active_record_orm_primals_controller.rb +36 -0
  58. data/spec/dummy/app/controllers/orms/mongoid_orm_primals_controller.rb +42 -0
  59. data/spec/dummy/app/models/active_record_orm.rb +5 -0
  60. data/spec/dummy/app/models/active_record_orm/primal.rb +2 -0
  61. data/spec/dummy/app/models/{mongoid_test.rb → mongoid_orm.rb} +1 -1
  62. data/spec/dummy/app/models/mongoid_orm/primal.rb +46 -0
  63. data/spec/dummy/config/environments/development.rb +3 -0
  64. data/spec/dummy/config/routes.rb +2 -1
  65. data/spec/dummy/db/migrate/20110930183902_create_active_record_orm_primals.rb +17 -0
  66. data/spec/dummy/db/schema.rb +15 -1
  67. data/spec/fabricators/active_record_orm/primal.rb +13 -0
  68. data/spec/fabricators/mongoid_orm/primal.rb +12 -0
  69. data/spec/generators/puffer/component/component_generator_spec.rb +32 -0
  70. data/spec/generators/puffer/controller/controller_generator_spec.rb +31 -0
  71. data/spec/helpers/puffer_helper_spec.rb +62 -0
  72. data/spec/lib/filters_spec.rb +21 -0
  73. data/spec/lib/orm_adapter/active_record_spec.rb +33 -0
  74. data/spec/lib/orm_adapter/base_shared.rb +97 -0
  75. data/spec/lib/orm_adapter/mongoid_spec.rb +46 -0
  76. data/spec/lib/resource/routing_spec.rb +5 -5
  77. data/spec/lib/resource/tree_spec.rb +31 -0
  78. data/spec/lib/resource_spec.rb +36 -37
  79. data/spec/spec_helper.rb +3 -2
  80. metadata +71 -43
  81. data/app/views/puffer/base/_form.html.erb +0 -11
  82. data/lib/generators/puffer/component/templates/component_spec.rb +0 -19
  83. data/lib/puffer/resource/scoping.rb +0 -19
  84. data/spec/dummy/app/controllers/orms/mongoid_tests_controller.rb +0 -19
  85. data/spec/lib/params_spec.rb +0 -120
@@ -45,9 +45,7 @@ module Puffer
45
45
  end
46
46
 
47
47
  def custom_type
48
- return :select if options.key?(:select)
49
- return :password if name =~ /password/
50
- return reflection.macro if reflection
48
+ Puffer.field_type_for self
51
49
  end
52
50
 
53
51
  def reflection
@@ -59,7 +57,7 @@ module Puffer
59
57
  end
60
58
 
61
59
  def component_class
62
- @component_class ||= Puffer::Component.component_for type
60
+ @component_class ||= Puffer.component_for self
63
61
  end
64
62
 
65
63
  def component
@@ -13,25 +13,13 @@ module Puffer
13
13
  last
14
14
  end
15
15
 
16
- def searchable
17
- @searchable ||= map { |f| f if f.column && [:text, :string, :integer, :decimal, :float].include?(f.column_type) }.compact
18
- end
19
-
20
- def searches query
21
- searchable.map { |f| "#{f.query_column} like '%#{query}%'" if f.query_column.present? }.compact.join(' or ') if query
22
- end
23
-
24
- def boolean
25
- @boolean ||= reject { |f| f.type != :boolean }
26
- end
27
-
28
- def includes
29
- @includes ||= map {|f| f.path unless f.native?}.compact.to_includes
16
+ def columns
17
+ select {|f| f.column}.to_fieldset
30
18
  end
31
19
 
32
20
  def [] key
33
21
  if key.is_a?(String) || key.is_a?(Symbol)
34
- detect {|f| f.to_s == key.to_s}
22
+ detect {|f| f.field_name == key.to_s}
35
23
  else
36
24
  super
37
25
  end
@@ -0,0 +1,105 @@
1
+ module Puffer
2
+ class Filters
3
+ include ActiveModel::Conversion
4
+ extend ActiveModel::Naming
5
+ extend ActiveModel::Translation
6
+ include ActiveModel::AttributeMethods
7
+
8
+ delegate :model_name, :to => 'self.class'
9
+
10
+ def initialize attributes = {}
11
+ attributes ||= {}
12
+ attributes.each do |(key, value)|
13
+ send("#{key}=", value) if respond_to?("#{key}=")
14
+ end
15
+ end
16
+
17
+ def read_attribute name
18
+ attributes[name]
19
+ end
20
+
21
+ def write_attribute name, value
22
+ attributes[name] = value.presence if attributes.key?(name)
23
+ end
24
+
25
+ def persisted?
26
+ false
27
+ end
28
+
29
+ def any?
30
+ attributes.keys.any? { |attribute| !send(attribute).nil? }
31
+ end
32
+
33
+ def conditions
34
+ attributes.except('puffer_search', 'puffer_order').keys.reduce({}) do |res, attribute|
35
+ value = send(attribute)
36
+
37
+ unless value.nil?
38
+ value = case value
39
+ when 'puffer_nil' then nil
40
+ when 'puffer_blank' then ''
41
+ else value
42
+ end
43
+ res[attribute] = value
44
+ end
45
+
46
+ res
47
+ end
48
+ end
49
+
50
+ def search
51
+ puffer_search
52
+ end
53
+
54
+ def order
55
+ puffer_order
56
+ end
57
+
58
+ module ClassMethods
59
+ def model_name
60
+ @model_name ||= begin
61
+ name = super
62
+ name.instance_variable_set :@param_key, 'filters'
63
+ name
64
+ end
65
+ end
66
+
67
+ def controller_filters controller
68
+ scope = "#{controller}Filters".split('::')[0..-2].join('::').constantize rescue Kernel
69
+ name = "#{controller}Filters".demodulize
70
+
71
+ if scope.const_defined?(name)
72
+ scope.const_get(name)
73
+ else
74
+ attributes_from_controller = controller.index_fields.reduce({}) do |res, field|
75
+ res[field.field_name] = nil
76
+ res
77
+ end
78
+
79
+ attributes_from_controller.merge!('puffer_search' => nil, 'puffer_order' => nil)
80
+
81
+ klass = Class.new(self)
82
+
83
+ attributes_from_controller.keys.each do |name|
84
+ klass.send :define_method, name do
85
+ read_attribute name
86
+ end
87
+ klass.send :define_method, "#{name}=" do |value|
88
+ write_attribute name, value
89
+ end
90
+ end
91
+
92
+ klass.send :define_method, :attributes do
93
+ attributes_from_controller
94
+ end
95
+
96
+
97
+ scope.const_set(name, klass)
98
+ end
99
+ end
100
+ end
101
+
102
+ extend ClassMethods
103
+
104
+ end
105
+ end
@@ -8,6 +8,39 @@ module Puffer
8
8
  end
9
9
  end
10
10
 
11
+ def filter scope, fields, options = {}
12
+ fields = fields.columns
13
+ conditions, order = extract_conditions_and_order!(options)
14
+
15
+ conditions_fields = fields.select {|f| conditions.keys.include?(f.field_name)}.to_fieldset
16
+ search_fields = fields.select {|f| !conditions_fields.include?(f) && search_types.include?(f.column_type)}
17
+ all_fields = conditions_fields + search_fields
18
+
19
+ conditions = conditions.reduce({}) do |res, (name, value)|
20
+ field = conditions_fields[name]
21
+ res[field.query_column] = value if field
22
+ res
23
+ end
24
+
25
+ order = order.map {|field, direction| "#{field} #{direction}"}.join(', ')
26
+
27
+ scope.includes(includes(all_fields)).where(searches(search_fields, options[:search])).where(conditions).order(order)
28
+ end
29
+
30
+ private
31
+
32
+ def search_types
33
+ [:text, :string, :integer, :decimal, :float]
34
+ end
35
+
36
+ def includes fields
37
+ fields.map {|f| f.path unless f.native?}.compact.to_includes
38
+ end
39
+
40
+ def searches fields, query
41
+ [fields.map {|f| "#{f.query_column} like ?"}.compact.join(' or '), *(Array.wrap("%#{query}%") * fields.count)] if query.present?
42
+ end
43
+
11
44
  end
12
45
  end
13
46
  end
@@ -6,6 +6,10 @@ module Puffer
6
6
  raise NotSupportedError
7
7
  end
8
8
 
9
+ def filter scope, fields, options = {}
10
+ raise NotSupportedError
11
+ end
12
+
9
13
  end
10
14
  end
11
15
  end
@@ -8,6 +8,35 @@ module Puffer
8
8
  end
9
9
  end
10
10
 
11
+ def filter scope, fields, options = {}
12
+ fields = fields.columns
13
+ conditions, order = extract_conditions_and_order!(options)
14
+
15
+ conditions_fields = fields.select {|f| conditions.keys.include?(f.field_name)}.to_fieldset
16
+ search_fields = fields.select {|f| !conditions_fields.include?(f) && search_types.include?(f.column_type)}
17
+ all_fields = conditions_fields + search_fields
18
+
19
+ conditions = conditions.reduce({}) do |res, (name, value)|
20
+ field = conditions_fields[name]
21
+ res[field.name] = value if field
22
+ res
23
+ end
24
+
25
+ scope = scope.any_of(searches(search_fields, options[:search])) if options[:search].present?
26
+ scope.where(conditions).order(order)
27
+ end
28
+
29
+ private
30
+
31
+ def search_types
32
+ [:string, :integer, :big_decimal, :float, :"bson/object_id", :symbol]
33
+ end
34
+
35
+ def searches fields, query
36
+ regexp = /#{Regexp.escape(query)}/i
37
+ fields.map {|field| {field.name => regexp}}
38
+ end
39
+
11
40
  end
12
41
  end
13
42
  end
@@ -9,57 +9,25 @@ module Puffer
9
9
  class Resource
10
10
 
11
11
  include Routing
12
- include Scoping
13
12
 
14
- attr_reader :controller, :params, :namespace, :action, :controller_name, :model_name, :controller_class, :model
15
- delegate :env, :request, :to => :controller, :allow_nil => true
13
+ attr_reader :resource_node, :scope, :params, :controller_instance
14
+ delegate :controller, :name, :plural?, :to => :resource_node, :allow_nil => true
15
+ delegate :model, :to => :controller, :allow_nil => true
16
+ delegate :env, :request, :to => :controller_instance, :allow_nil => true
16
17
 
17
- def initialize params, controller = nil
18
+ def initialize params, controller_instance = nil
18
19
  params = ActiveSupport::HashWithIndifferentAccess.new.deep_merge params
19
- @action = params.delete :action
20
- @controller_class = "#{params.delete :controller}_controller".camelize.constantize
21
- @controller_name = controller_class.controller_name
22
- @namespace = controller_class.namespace
23
- @model_name = controller_class.model_name if controller_class.puffer?
24
- @model = controller_class.model if controller_class.puffer?
25
- @params = params
26
- @controller = controller
27
- end
28
20
 
29
- def plural?
30
- params[:plural]
21
+ @resource_node = params[:puffer]
22
+ @scope = swallow_nil{@resource_node.scope} || params[:puffer_scope]
23
+ @params = params
24
+ @controller_instance = controller_instance
31
25
  end
32
26
 
33
27
  def human
34
28
  model.model_name.human
35
29
  end
36
30
 
37
- def parent
38
- @parent ||= begin
39
- parent_ancestors = params[:ancestors].dup rescue []
40
- parent_name = parent_ancestors.pop
41
- if parent_name
42
- parent_params = ActiveSupport::HashWithIndifferentAccess.new({
43
- :controller => "#{namespace}/#{parent_name.to_s.pluralize}",
44
- :action => 'index',
45
- :plural => parent_name.plural?,
46
- :ancestors => parent_ancestors,
47
- :children => []
48
- })
49
-
50
- parent_ancestors.each do |ancestor|
51
- key = ancestor.to_s.singularize.foreign_key
52
- parent_params.merge! key => params[key] if params[key]
53
- end
54
- parent_params.merge! :id => params[parent_name.to_s.singularize.foreign_key]
55
-
56
- self.class.new parent_params, controller
57
- else
58
- nil
59
- end
60
- end
61
- end
62
-
63
31
  def ancestors
64
32
  @ancestors ||= begin
65
33
  ancestors = []
@@ -75,40 +43,73 @@ module Puffer
75
43
  @root ||= (ancestors.first || self)
76
44
  end
77
45
 
78
- def children(custom_params = {})
79
- @children ||= params[:children].map do |child_name|
80
- child_params = ActiveSupport::HashWithIndifferentAccess.new(custom_params.deep_merge({
81
- :controller => "#{namespace}/#{child_name.to_s.pluralize}",
82
- :action => 'index',
83
- :plural => child_name.plural?,
84
- :ancestors => params[:ancestors].dup.push((plural? ? controller_name : controller_name.singularize).to_sym),
85
- :children => []
86
- }))
87
-
88
- params[:ancestors].each do |ancestor|
46
+ def parent
47
+ @parent ||= begin
48
+ if resource_node.parent
49
+ parent_params = ActiveSupport::HashWithIndifferentAccess.new({:puffer => resource_node.parent})
50
+
51
+ resource_node.ancestors[0..-2].each do |ancestor|
52
+ key = ancestor.to_s.singularize.foreign_key
53
+ parent_params[key] = params[key] if params[key]
54
+ key = key.gsub(/_id$/, '_member')
55
+ parent_params[key] = params[key] if params[key]
56
+ end
57
+
58
+ key = resource_node.parent.to_s.singularize.foreign_key
59
+ parent_params[:id] = params[key] if params[key]
60
+ key = key.gsub(/_id$/, '_member')
61
+ parent_params[:member] = params[key] if params[key]
62
+
63
+ self.class.new parent_params, controller_instance
64
+ end
65
+ end
66
+ end
67
+
68
+ def children
69
+ @children ||= resource_node.children.map do |child_node|
70
+ child_params = ActiveSupport::HashWithIndifferentAccess.new({:puffer => child_node})
71
+
72
+ resource_node.ancestors.each do |ancestor|
89
73
  key = ancestor.to_s.singularize.foreign_key
90
- child_params.merge! key => params[key] if params[key]
74
+ child_params[key] = params[key] if params[key]
75
+ key = key.gsub(/_id$/, '_member')
76
+ child_params[key] = params[key] if params[key]
91
77
  end
92
- child_params.merge! controller_name.singularize.foreign_key => params[:id] if params[:id]
93
78
 
94
- self.class.new child_params, controller
79
+ key = resource_node.to_s.singularize.foreign_key
80
+ child_params[key] = params[:id] if params[:id]
81
+ key = key.gsub(/_id$/, '_member')
82
+ child_params[key] = params[:member] if params[:member]
83
+
84
+ self.class.new child_params, controller_instance
85
+ end
86
+ end
87
+
88
+ def children_hash
89
+ @children_hash ||= children.inject(ActiveSupport::HashWithIndifferentAccess.new) do |result, child|
90
+ result[child.name] = child
91
+ result
95
92
  end
96
93
  end
97
94
 
98
95
  def collection_scope
99
- parent ? parent.member.send(model_name.pluralize) : model
96
+ parent ? parent.member.send(name) : model
100
97
  end
101
98
 
102
99
  def collection
103
- collection_scope.includes(includes).where(searches(params[:search])).order(order).page(params[:page])
100
+ model.to_adapter.filter collection_scope, controller.index_fields,
101
+ :conditions => controller_instance.puffer_filters.conditions,
102
+ :search => controller_instance.puffer_filters.search,
103
+ :order => controller_instance.puffer_filters.order
104
104
  end
105
105
 
106
106
  def member
107
+ return params[:member] if params[:member]
107
108
  if parent
108
109
  if plural?
109
- parent.member.send(model_name.pluralize).find params[:id] if params[:id]
110
+ parent.member.send(name).find params[:id] if params[:id]
110
111
  else
111
- parent.member.send(model_name)
112
+ parent.member.send(name)
112
113
  end
113
114
  else
114
115
  model.find params[:id] if params[:id]
@@ -118,9 +119,9 @@ module Puffer
118
119
  def new_member
119
120
  if parent
120
121
  if plural?
121
- parent.member.send(model_name.pluralize).new attributes
122
+ parent.member.send(name).new attributes
122
123
  else
123
- parent.member.send("build_#{model_name}", attributes)
124
+ parent.member.send("build_#{name}", attributes)
124
125
  end
125
126
  else
126
127
  model.new attributes
@@ -128,7 +129,7 @@ module Puffer
128
129
  end
129
130
 
130
131
  def attributes
131
- params[model_name]
132
+ params[name.to_s.singularize]
132
133
  end
133
134
 
134
135
  def method_missing method, *args, &block
@@ -0,0 +1,71 @@
1
+ module Puffer
2
+ class Resource
3
+ class Node
4
+ attr_accessor :parent, :children, :options
5
+
6
+ def initialize *args
7
+ @options = args.extract_options!
8
+ @parent = args.first
9
+ @children = []
10
+ @parent.children.push self if parent
11
+ end
12
+
13
+ def name
14
+ options[:name]
15
+ end
16
+
17
+ def to_s
18
+ name.to_s
19
+ end
20
+
21
+ def scope
22
+ options[:scope]
23
+ end
24
+
25
+ def controller
26
+ options[:controller]
27
+ end
28
+
29
+ def group
30
+ controller.configuration.group
31
+ end
32
+
33
+ def singular
34
+ name.to_s.singularize
35
+ end
36
+
37
+ def singular?
38
+ options[:singular]
39
+ end
40
+
41
+ def plural
42
+ name.to_s.pluralize
43
+ end
44
+
45
+ def plural?
46
+ !singular?
47
+ end
48
+
49
+ def root?
50
+ parent.nil?
51
+ end
52
+
53
+ def ancestors
54
+ ancestors = []
55
+ resource = self
56
+ while resource = resource.parent do
57
+ ancestors.unshift resource
58
+ end
59
+ ancestors
60
+ end
61
+
62
+ def url_string
63
+ [scope, name, (:index if singular == plural)].compact.map(&:to_s).join('_')
64
+ end
65
+
66
+ def to_struct
67
+ {:scope => scope, :current => name, :children => children.map(&:name), :ancestors => ancestors.map(&:name)}
68
+ end
69
+ end
70
+ end
71
+ end