puffer 0.0.29 → 0.0.30

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.
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