active_scaffold 3.0.23 → 3.0.24

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. data/frontends/default/views/_action_group.html.erb +1 -1
  2. data/frontends/default/views/_action_group.html.erb~ +24 -0
  3. data/frontends/default/views/_form.html.erb~ +26 -0
  4. data/frontends/default/views/_form_association.html.erb~ +19 -0
  5. data/frontends/default/views/_form_association_footer.html.erb~ +16 -6
  6. data/frontends/default/views/_horizontal_subform.html.erb~ +29 -0
  7. data/frontends/default/views/_horizontal_subform_header.html.erb~ +3 -2
  8. data/frontends/default/views/_list_actions.html.erb~ +15 -0
  9. data/frontends/default/views/_list_inline_adapter.html.erb~ +10 -0
  10. data/frontends/default/views/_list_messages.html.erb~ +30 -0
  11. data/frontends/default/views/_list_pagination.html.erb~ +11 -0
  12. data/frontends/default/views/_list_pagination_links.html.erb~ +0 -0
  13. data/frontends/default/views/_render_field.js.erb~ +23 -0
  14. data/frontends/default/views/_row.html.erb~ +6 -0
  15. data/frontends/default/views/_vertical_subform.html.erb~ +12 -0
  16. data/frontends/default/views/edit_associated.js.erb~ +13 -0
  17. data/frontends/default/views/on_create.js.rjs +2 -2
  18. data/frontends/default/views/render_field.js.erb~ +1 -0
  19. data/lib/active_scaffold/actions/core.rb~ +13 -5
  20. data/lib/active_scaffold/actions/create.rb~ +149 -0
  21. data/lib/active_scaffold/actions/list.rb~ +196 -0
  22. data/lib/active_scaffold/actions/nested.rb +6 -2
  23. data/lib/active_scaffold/actions/nested.rb~ +252 -0
  24. data/lib/active_scaffold/actions/search.rb~ +49 -0
  25. data/lib/active_scaffold/actions/subform.rb~ +27 -0
  26. data/lib/active_scaffold/actions/update.rb~ +149 -0
  27. data/lib/active_scaffold/attribute_params.rb~ +202 -0
  28. data/lib/active_scaffold/bridges/record_select/{lib/record_select_bridge.rb~ → helpers.rb~} +7 -16
  29. data/lib/active_scaffold/bridges/shared/date_bridge.rb~ +209 -0
  30. data/lib/active_scaffold/config/create.rb +4 -4
  31. data/lib/active_scaffold/config/nested.rb +1 -0
  32. data/lib/active_scaffold/config/nested.rb~ +41 -0
  33. data/lib/active_scaffold/config/search.rb~ +74 -0
  34. data/lib/active_scaffold/constraints.rb~ +186 -0
  35. data/lib/active_scaffold/data_structures/action_columns.rb~ +140 -0
  36. data/lib/active_scaffold/data_structures/action_link.rb +4 -4
  37. data/lib/active_scaffold/data_structures/action_link.rb~ +179 -0
  38. data/lib/active_scaffold/data_structures/nested_info.rb~ +124 -0
  39. data/lib/active_scaffold/extensions/action_controller_rendering.rb~ +22 -0
  40. data/lib/active_scaffold/extensions/action_view_rendering.rb~ +108 -0
  41. data/lib/active_scaffold/extensions/cache_association.rb~ +12 -0
  42. data/lib/active_scaffold/extensions/reverse_associations.rb~ +64 -0
  43. data/lib/active_scaffold/extensions/routing_mapper.rb~ +34 -0
  44. data/lib/active_scaffold/extensions/unsaved_associated.rb~ +62 -0
  45. data/lib/active_scaffold/finder.rb~ +370 -0
  46. data/lib/active_scaffold/helpers/controller_helpers.rb~ +101 -0
  47. data/lib/active_scaffold/helpers/form_column_helpers.rb~ +321 -0
  48. data/lib/active_scaffold/helpers/id_helpers.rb~ +123 -0
  49. data/lib/active_scaffold/helpers/list_column_helpers.rb +9 -6
  50. data/lib/active_scaffold/helpers/list_column_helpers.rb~ +368 -0
  51. data/lib/active_scaffold/helpers/search_column_helpers.rb~ +94 -46
  52. data/lib/active_scaffold/helpers/view_helpers.rb +1 -1
  53. data/lib/active_scaffold/helpers/view_helpers.rb~ +353 -0
  54. data/lib/active_scaffold/version.rb +1 -1
  55. data/lib/active_scaffold.rb +1 -1
  56. data/lib/active_scaffold.rb~ +362 -0
  57. metadata +110 -76
  58. data/lib/active_scaffold/bridges/dragonfly/bridge.rb~ +0 -12
  59. data/lib/active_scaffold/bridges/dragonfly/lib/dragonfly_bridge.rb~ +0 -36
  60. data/lib/active_scaffold/bridges/dragonfly/lib/dragonfly_bridge_helpers.rb~ +0 -12
  61. data/lib/active_scaffold/bridges/dragonfly/lib/form_ui.rb~ +0 -27
  62. data/lib/active_scaffold/bridges/dragonfly/lib/list_ui.rb~ +0 -16
@@ -0,0 +1,186 @@
1
+ module ActiveScaffold
2
+ module Constraints
3
+
4
+ protected
5
+
6
+ # Returns the current constraints
7
+ def active_scaffold_constraints
8
+ @active_scaffold_constraints ||= active_scaffold_session_storage[:constraints] || {}
9
+ end
10
+
11
+ def set_active_scaffold_constraints
12
+ associations_by_params = {}
13
+ active_scaffold_config.model.reflect_on_all_associations.each do |association|
14
+ associations_by_params[association.klass.name.foreign_key] = association.name unless association.options[:polymorphic]
15
+ end
16
+ params.each do |key, value|
17
+ active_scaffold_constraints[associations_by_params[key]] = value if associations_by_params.include? key
18
+ end
19
+ end
20
+
21
+ # For each enabled action, adds the constrained columns to the ActionColumns object (if it exists).
22
+ # This lets the ActionColumns object skip constrained columns.
23
+ #
24
+ # If the constraint value is a Hash, then we assume the constraint is a multi-level association constraint (the reverse of a has_many :through) and we do NOT register the constraint column.
25
+ def register_constraints_with_action_columns(association_constrained_fields = [], exclude_actions = [])
26
+ debugger
27
+ constrained_fields = active_scaffold_constraints.reject{|k, v| v.is_a? Hash}.keys.collect{|k| k.to_sym}
28
+ constrained_fields = constrained_fields | association_constrained_fields
29
+ if self.class.uses_active_scaffold?
30
+ # we actually want to do this whether constrained_fields exist or not, so that we can reset the array when they don't
31
+ active_scaffold_config.actions.each do |action_name|
32
+ next if exclude_actions.include?(action_name)
33
+ action = active_scaffold_config.send(action_name)
34
+ next unless action.respond_to? :columns
35
+ action.columns.constraint_columns = constrained_fields
36
+ end
37
+ end
38
+ end
39
+
40
+ # Returns search conditions based on the current scaffold constraints.
41
+ #
42
+ # Supports constraints based on either a column name (in which case it checks for an association
43
+ # or just uses the search_sql) or a database field name.
44
+ #
45
+ # All of this work is primarily to support nested scaffolds in a manner generally useful for other
46
+ # embedded scaffolds.
47
+ def conditions_from_constraints
48
+ conditions = nil
49
+ debugger
50
+ active_scaffold_constraints.each do |k, v|
51
+ column = active_scaffold_config.columns[k]
52
+ constraint_condition = if column
53
+ # Assume this is a multi-level association constraint.
54
+ # example:
55
+ # data model: Park -> Den -> Bear
56
+ # constraint: :den => {:park => 5}
57
+ if v.is_a? Hash
58
+ far_association = column.association.klass.reflect_on_association(v.keys.first)
59
+ field = far_association.klass.primary_key
60
+ table = far_association.table_name
61
+
62
+ active_scaffold_includes.concat([{k => v.keys.first}]) # e.g. {:den => :park}
63
+ constraint_condition_for("#{table}.#{field}", v.values.first)
64
+
65
+ # association column constraint
66
+ elsif column.association
67
+ if column.association.macro == :has_and_belongs_to_many
68
+ active_scaffold_habtm_joins.concat column.includes
69
+ else
70
+ active_scaffold_includes.concat column.includes
71
+ end
72
+ condition_from_association_constraint(column.association, v)
73
+
74
+ # regular column constraints
75
+ elsif column.searchable?
76
+ active_scaffold_includes.concat column.includes
77
+ constraint_condition_for(column.search_sql, v)
78
+ end
79
+ # unknown-to-activescaffold-but-real-database-column constraint
80
+ elsif active_scaffold_config.model.column_names.include? k.to_s
81
+ constraint_condition_for(k.to_s, v)
82
+ else
83
+ raise ActiveScaffold::MalformedConstraint, constraint_error(active_scaffold_config.model, k), caller
84
+ end
85
+
86
+ conditions = merge_conditions(conditions, constraint_condition)
87
+ end
88
+
89
+ conditions
90
+ end
91
+
92
+ # We do NOT want to use .search_sql. If anything, search_sql will refer
93
+ # to a human-searchable value on the associated record.
94
+ def condition_from_association_constraint(association, value)
95
+ # when the reverse association is a :belongs_to, the id for the associated object only exists as
96
+ # the primary_key on the other table. so for :has_one and :has_many (when the reverse is :belongs_to),
97
+ # we have to use the other model's primary_key.
98
+ #
99
+ # please see the relevant tests for concrete examples.
100
+ field = if [:has_one, :has_many].include?(association.macro)
101
+ association.klass.primary_key
102
+ elsif [:has_and_belongs_to_many].include?(association.macro)
103
+ association.association_foreign_key
104
+ else
105
+ association.options[:foreign_key] || association.name.to_s.foreign_key
106
+ end
107
+
108
+ table = case association.macro
109
+ when :has_and_belongs_to_many
110
+ association.options[:join_table]
111
+
112
+ when :belongs_to
113
+ active_scaffold_config.model.table_name
114
+
115
+ else
116
+ association.table_name
117
+ end
118
+
119
+ if association.options[:primary_key]
120
+ value = association.klass.find(value).send(association.options[:primary_key])
121
+ end
122
+
123
+ condition = constraint_condition_for("#{table}.#{field}", value)
124
+ if association.options[:polymorphic]
125
+ begin
126
+ parent_scaffold = "#{session_info[:parent_scaffold].to_s.camelize}Controller".constantize
127
+ condition = merge_conditions(
128
+ condition,
129
+ constraint_condition_for("#{table}.#{association.name}_type", parent_scaffold.active_scaffold_config.model_id.to_s)
130
+ )
131
+ rescue ActiveScaffold::ControllerNotFound
132
+ nil
133
+ end
134
+ end
135
+
136
+ condition
137
+ end
138
+
139
+ def constraint_error(klass, column_name)
140
+ "Malformed constraint `#{klass}##{column_name}'. If it's a legitimate column, and you are using a nested scaffold, please specify or double-check the reverse association name."
141
+ end
142
+
143
+ # Applies constraints to the given record.
144
+ #
145
+ # Searches through the known columns for association columns. If the given constraint is an association,
146
+ # it assumes that the constraint value is an id. It then does a association.klass.find with the value
147
+ # and adds the associated object to the record.
148
+ #
149
+ # For some operations ActiveRecord will automatically update the database. That's not always ok.
150
+ # If it *is* ok (e.g. you're in a transaction), then set :allow_autosave to true.
151
+ def apply_constraints_to_record(record, options = {})
152
+ options[:allow_autosave] = false if options[:allow_autosave].nil?
153
+
154
+ active_scaffold_constraints.each do |k, v|
155
+ column = active_scaffold_config.columns[k]
156
+ if column and column.association
157
+ if column.plural_association?
158
+ record.send("#{k}").send(:<<, column.association.klass.find(v))
159
+ elsif column.association.options[:polymorphic]
160
+ record.send("#{k}=", params[:parent_model].constantize.find(v))
161
+ else # regular singular association
162
+ record.send("#{k}=", column.association.klass.find(v))
163
+
164
+ # setting the belongs_to side of a has_one isn't safe. if the has_one was already
165
+ # specified, rails won't automatically clear out the previous associated record.
166
+ #
167
+ # note that we can't take the extra step to correct this unless we're permitted to
168
+ # run operations where activerecord auto-saves the object.
169
+ reverse = column.association.klass.reflect_on_association(column.association.reverse)
170
+ if reverse.macro == :has_one and options[:allow_autosave]
171
+ record.send(k).send("#{column.association.reverse}=", record)
172
+ end
173
+ end
174
+ else
175
+ record.send("#{k}=", v)
176
+ end
177
+ end
178
+ end
179
+
180
+ private
181
+
182
+ def constraint_condition_for(sql, value)
183
+ value.nil? ? "#{sql} IS NULL" : ["#{sql} = ?", value]
184
+ end
185
+ end
186
+ end
@@ -0,0 +1,140 @@
1
+ module ActiveScaffold::DataStructures
2
+ # A set of columns. These structures can be nested for organization.
3
+ class ActionColumns < ActiveScaffold::DataStructures::Set
4
+ include ActiveScaffold::Configurable
5
+
6
+ # this lets us refer back to the action responsible for this link, if it exists.
7
+ # the immediate need here is to get the crud_type so we can dynamically filter columns from the set.
8
+ attr_accessor :action
9
+
10
+ # labels are useful for the Create/Update forms, when we display columns in a grouped fashion and want to name them separately
11
+ attr_writer :label
12
+ def label
13
+ as_(@label) if @label
14
+ end
15
+ def name
16
+ @label
17
+ end
18
+
19
+ # Whether this column set is collapsed by default in contexts where collapsing is supported
20
+ attr_accessor :collapsed
21
+
22
+ # nests a subgroup in the column set
23
+ def add_subgroup(label, &proc)
24
+ columns = ActiveScaffold::DataStructures::ActionColumns.new
25
+ columns.label = label
26
+ columns.action = self.action
27
+ columns.configure &proc
28
+ self.exclude columns.collect_columns
29
+ self.add columns
30
+ end
31
+
32
+ def include?(item)
33
+ @set.each do |c|
34
+ return true if !c.is_a? Symbol and c.include? item
35
+ return true if c == item.to_sym
36
+ end
37
+ return false
38
+ end
39
+
40
+ def names
41
+ self.collect(&:name)
42
+ end
43
+
44
+ def names_without_auth_check
45
+ Array(@set)
46
+ end
47
+
48
+ protected
49
+
50
+ def collect_columns
51
+ @set.collect {|col| col.is_a?(ActiveScaffold::DataStructures::ActionColumns) ? col.collect_columns : col}
52
+ end
53
+
54
+ # called during clone or dup. makes the clone/dup deeper.
55
+ def initialize_copy(from)
56
+ @set = from.instance_variable_get('@set').clone
57
+ end
58
+
59
+ # A package of stuff to add after the configuration block. This is an attempt at making a certain level of functionality inaccessible during configuration, to reduce possible breakage from misuse.
60
+ # The bulk of the package is a means of connecting the referential column set (ActionColumns) with the actual column objects (Columns). This lets us iterate over the set and yield real column objects.
61
+ module AfterConfiguration
62
+ # Redefine the each method to yield actual Column objects.
63
+ # It will skip constrained and unauthorized columns.
64
+ #
65
+ # Options:
66
+ # * :flatten - whether to recursively iterate on nested sets. default is false.
67
+ # * :for - the record (or class) being iterated over. used for column-level security. default is the class.
68
+ def each(options = {}, &proc)
69
+ options[:for] ||= @columns.active_record_class
70
+ self.unauthorized_columns = []
71
+ @set.each do |item|
72
+ unless item.is_a? ActiveScaffold::DataStructures::ActionColumns
73
+ item = (@columns[item] || ActiveScaffold::DataStructures::Column.new(item.to_sym, @columns.active_record_class))
74
+ next if self.skip_column?(item, options)
75
+ end
76
+ if item.is_a? ActiveScaffold::DataStructures::ActionColumns and options.has_key?(:flatten) and options[:flatten]
77
+ item.each(options, &proc)
78
+ else
79
+ yield item
80
+ end
81
+ end
82
+ end
83
+
84
+ def collect_visible(options = {}, &proc)
85
+ columns = []
86
+ options[:for] ||= @columns.active_record_class
87
+ self.unauthorized_columns = []
88
+ @set.each do |item|
89
+ unless item.is_a? ActiveScaffold::DataStructures::ActionColumns
90
+ item = (@columns[item] || ActiveScaffold::DataStructures::Column.new(item.to_sym, @columns.active_record_class))
91
+ next if self.skip_column?(item, options)
92
+ end
93
+ if item.is_a? ActiveScaffold::DataStructures::ActionColumns and options.has_key?(:flatten) and options[:flatten]
94
+ columns = columns + item.collect(options, &proc)
95
+ else
96
+ columns << item
97
+ end
98
+ end
99
+ columns
100
+ end
101
+
102
+ def skip_column?(column, options)
103
+ result = false
104
+ # skip if this matches a constrained column
105
+ result = true if constraint_columns.include?(column.name.to_sym)
106
+ # skip if this matches the field_name of a constrained column
107
+ result = true if column.field_name and constraint_columns.include?(column.field_name.to_sym)
108
+ # skip this field if it's not authorized
109
+ unless options[:for].authorized_for?(:action => options[:action], :crud_type => options[:crud_type] || self.action.crud_type, :column => column.name)
110
+ self.unauthorized_columns << column.name.to_sym
111
+ result = true
112
+ end
113
+ return result
114
+ end
115
+
116
+ # registers a set of column objects (recursively, for all nested ActionColumns)
117
+ def set_columns(columns)
118
+ @columns = columns
119
+ # iterate over @set instead of self to avoid dealing with security queries
120
+ @set.each do |item|
121
+ item.set_columns(columns) if item.respond_to? :set_columns
122
+ end
123
+ end
124
+
125
+ attr_writer :constraint_columns
126
+ def constraint_columns
127
+ @constraint_columns ||= []
128
+ end
129
+
130
+ attr_writer :unauthorized_columns
131
+ def unauthorized_columns
132
+ @unauthorized_columns ||= []
133
+ end
134
+
135
+ def length
136
+ ((@set - self.constraint_columns) - self.unauthorized_columns).length
137
+ end
138
+ end
139
+ end
140
+ end
@@ -3,15 +3,15 @@ module ActiveScaffold::DataStructures
3
3
  # provides a quick way to set any property of the object from a hash
4
4
  def initialize(action, options = {})
5
5
  # set defaults
6
- self.action = action.to_s
6
+ self.action = action
7
7
  self.label = action
8
8
  self.confirm = false
9
9
  self.type = :collection
10
10
  self.inline = true
11
11
  self.method = :get
12
- self.crud_type = :delete if [:destroy].include?(action.to_sym)
13
- self.crud_type = :create if [:create, :new].include?(action.to_sym)
14
- self.crud_type = :update if [:edit, :update].include?(action.to_sym)
12
+ self.crud_type = :delete if [:destroy].include?(action.try(:to_sym))
13
+ self.crud_type = :create if [:create, :new].include?(action.try(:to_sym))
14
+ self.crud_type = :update if [:edit, :update].include?(action.try(:to_sym))
15
15
  self.crud_type ||= :read
16
16
  self.parameters = {}
17
17
  self.html_options = {}
@@ -0,0 +1,179 @@
1
+ module ActiveScaffold::DataStructures
2
+ class ActionLink
3
+ # provides a quick way to set any property of the object from a hash
4
+ def initialize(action, options = {})
5
+ # set defaults
6
+ self.action = action.to_s
7
+ self.label = action
8
+ self.confirm = false
9
+ self.type = :collection
10
+ self.inline = true
11
+ self.method = :get
12
+ self.crud_type = :delete if [:destroy].include?(action.try(:to_sym))
13
+ self.crud_type = :create if [:create, :new].include?(action.try(:to_sym))
14
+ self.crud_type = :update if [:edit, :update].include?(action.try(:to_sym))
15
+ self.crud_type ||= :read
16
+ self.parameters = {}
17
+ self.html_options = {}
18
+ self.column = nil
19
+ self.image = nil
20
+ self.dynamic_parameters = nil
21
+
22
+ # apply quick properties
23
+ options.each_pair do |k, v|
24
+ setter = "#{k}="
25
+ self.send(setter, v) if self.respond_to? setter
26
+ end
27
+ end
28
+
29
+ # the action-path for this link. what page to request? this is required!
30
+ attr_accessor :action
31
+
32
+ # the controller for this action link. if nil, the current controller should be assumed.
33
+ attr_writer :controller
34
+
35
+ def controller
36
+ @controller = @controller.call if @controller.is_a?(Proc)
37
+ @controller
38
+ end
39
+
40
+ def static_controller?
41
+ !(@controller.is_a?(Proc) || (@controller == :polymorph))
42
+ end
43
+
44
+ # a hash of request parameters
45
+ attr_accessor :parameters
46
+
47
+ # a block for dynamic_parameters
48
+ attr_accessor :dynamic_parameters
49
+
50
+ # the RESTful method
51
+ attr_accessor :method
52
+
53
+ # what string to use to represent this action
54
+ attr_writer :label
55
+ def label
56
+ @label.is_a?(Symbol) ? as_(@label) : @label
57
+ end
58
+
59
+ # image to use {:name => 'arrow.png', :size => '16x16'}
60
+ attr_accessor :image
61
+
62
+ # if the action requires confirmation
63
+ def confirm=(value)
64
+ @dhtml_confirm = nil if value
65
+ @confirm = value
66
+ end
67
+ def confirm(label = '')
68
+ @confirm.is_a?(String) ? @confirm : as_(@confirm, :label => label)
69
+ end
70
+ def confirm?
71
+ !!@confirm
72
+ end
73
+
74
+ # if the action uses a DHTML based (i.e. 2-phase) confirmation
75
+ attr_accessor :dhtml_confirm
76
+ def dhtml_confirm=(value)
77
+ @confirm = nil if value
78
+ @dhtml_confirm = value
79
+ end
80
+ def dhtml_confirm?
81
+ !!@dhtml_confirm
82
+ end
83
+
84
+ # what method to call on the controller to see if this action_link should be visible
85
+ # note that this is only the UI part of the security. to prevent URL hax0rz, you also need security on requests (e.g. don't execute update method unless authorized).
86
+ attr_writer :security_method
87
+ def security_method
88
+ @security_method || "#{self.action}_authorized?"
89
+ end
90
+
91
+ def security_method_set?
92
+ !!@security_method
93
+ end
94
+
95
+ attr_accessor :ignore_method
96
+
97
+ # the crud type of the (eventual?) action. different than :method, because this crud action may not be imminent.
98
+ # this is used to determine record-level authorization (e.g. record.authorized_for?(:crud_type => link.crud_type).
99
+ # options are :create, :read, :update, and :delete
100
+ attr_accessor :crud_type
101
+
102
+ # an "inline" link is inserted into the existing page
103
+ # exclusive with popup? and page?
104
+ def inline=(val)
105
+ @inline = (val == true)
106
+ self.popup = self.page = false if @inline
107
+ end
108
+ def inline?; @inline end
109
+
110
+ # a "popup" link displays in a separate (browser?) window. this will eventually take arguments.
111
+ # exclusive with inline? and page?
112
+ def popup=(val)
113
+ @popup = (val == true)
114
+ if @popup
115
+ self.inline = self.page = false
116
+
117
+ # the :method parameter doesn't mix with the :popup parameter
118
+ # when/if we start using DHTML popups, we can bring :method back
119
+ self.method = nil
120
+ end
121
+ end
122
+ def popup?; @popup end
123
+
124
+ # a "page" link displays by reloading the current page
125
+ # exclusive with inline? and popup?
126
+ def page=(val)
127
+ @page = (val == true)
128
+ if @page
129
+ self.inline = self.popup = false
130
+
131
+ # when :method is defined, ActionView adds an onclick to use a form ...
132
+ # so it's best to just empty out :method whenever possible.
133
+ # we only ever need to know @method = :get for things that default to POST.
134
+ # the only things that default to POST are forms and ajax calls.
135
+ # when @page = true, we don't use ajax.
136
+ self.method = nil if method == :get
137
+ end
138
+ end
139
+ def page?; @page end
140
+
141
+ # where the result of this action should insert in the display.
142
+ # for :type => :collection, supported values are:
143
+ # :top
144
+ # :bottom
145
+ # :replace (for updating the entire table)
146
+ # false (no attempt at positioning)
147
+ # for :type => :member, supported values are:
148
+ # :before
149
+ # :replace
150
+ # :after
151
+ # false (no attempt at positioning)
152
+ attr_writer :position
153
+ def position
154
+ return @position unless @position.nil? or @position == true
155
+ return :replace if self.type == :member
156
+ return :top if self.type == :collection
157
+ raise "what should the default position be for #{self.type}?"
158
+ end
159
+
160
+ # what type of link this is. currently supported values are :collection and :member.
161
+ attr_accessor :type
162
+
163
+ # html options for the link
164
+ attr_accessor :html_options
165
+
166
+ # nested action_links are referencing a column
167
+ attr_accessor :column
168
+
169
+ # indicates that this a nested_link
170
+ def nested_link?
171
+ @column || (parameters && parameters[:named_scope])
172
+ end
173
+
174
+ # Internal use: generated eid for this action_link
175
+ attr_accessor :eid
176
+
177
+
178
+ end
179
+ end
@@ -0,0 +1,124 @@
1
+ module ActiveScaffold::DataStructures
2
+ class NestedInfo
3
+ def self.get(model, session_storage)
4
+ if session_storage[:nested].nil?
5
+ nil
6
+ else
7
+ session_info = session_storage[:nested].clone
8
+ begin
9
+ debugger
10
+ session_info[:parent_scaffold] = "#{session_info[:parent_scaffold].to_s.camelize}Controller".constantize
11
+ session_info[:parent_model] = session_info[:parent_scaffold].active_scaffold_config.model
12
+ session_info[:association] = session_info[:parent_model].reflect_on_association(session_info[:name])
13
+ unless session_info[:association].nil?
14
+ ActiveScaffold::DataStructures::NestedInfoAssociation.new(model, session_info)
15
+ else
16
+ ActiveScaffold::DataStructures::NestedInfoScope.new(model, session_info)
17
+ end
18
+ rescue ActiveScaffold::ControllerNotFound
19
+ nil
20
+ end
21
+ end
22
+ end
23
+
24
+ attr_accessor :association, :child_association, :parent_model, :parent_scaffold, :parent_id, :constrained_fields, :scope
25
+
26
+ def initialize(model, session_info)
27
+ @parent_model = session_info[:parent_model]
28
+ @parent_id = session_info[:parent_id]
29
+ @parent_scaffold = session_info[:parent_scaffold]
30
+ end
31
+
32
+ def new_instance?
33
+ result = @new_instance.nil?
34
+ @new_instance = false
35
+ result
36
+ end
37
+
38
+ def parent_scope
39
+ parent_model.find(parent_id)
40
+ end
41
+
42
+ def habtm?
43
+ false
44
+ end
45
+
46
+ def belongs_to?
47
+ false
48
+ end
49
+
50
+ def has_one?
51
+ false
52
+ end
53
+
54
+ def readonly?
55
+ false
56
+ end
57
+
58
+ def sorted?
59
+ false
60
+ end
61
+ end
62
+
63
+ class NestedInfoAssociation < NestedInfo
64
+ def initialize(model, session_info)
65
+ super(model, session_info)
66
+ @association = session_info[:association]
67
+ iterate_model_associations(model)
68
+ end
69
+
70
+ def habtm?
71
+ association.macro == :has_and_belongs_to_many
72
+ end
73
+
74
+ def belongs_to?
75
+ association.belongs_to?
76
+ end
77
+
78
+ def has_one?
79
+ association.macro == :has_one
80
+ end
81
+
82
+ def readonly?
83
+ if association.options.has_key? :readonly
84
+ association.options[:readonly]
85
+ else
86
+ association.options.has_key? :through
87
+ end
88
+ end
89
+
90
+ def sorted?
91
+ association.options.has_key? :order
92
+ end
93
+
94
+ def default_sorting
95
+ association.options[:order]
96
+ end
97
+
98
+ protected
99
+
100
+ def iterate_model_associations(model)
101
+ @constrained_fields = []
102
+ @constrained_fields << association.foreign_key.to_sym unless association.belongs_to?
103
+ model.reflect_on_all_associations.each do |current|
104
+ if !current.belongs_to? && association.foreign_key == current.association_foreign_key
105
+ constrained_fields << current.name.to_sym
106
+ @child_association = current if current.klass == @parent_model
107
+ end
108
+ if association.foreign_key == current.foreign_key
109
+ # show columns for has_many and has_one child associationes
110
+ constrained_fields << current.name.to_sym if current.belongs_to?
111
+ @child_association = current
112
+ end
113
+ end
114
+ end
115
+ end
116
+
117
+ class NestedInfoScope < NestedInfo
118
+ def initialize(model, session_info)
119
+ super(model, session_info)
120
+ @scope = session_info[:name]
121
+ @constrained_fields = []
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,22 @@
1
+ # wrap the action rendering for ActiveScaffold controllers
2
+ module ActionController #:nodoc:
3
+ class Base
4
+ def render_with_active_scaffold(*args, &block)
5
+ if self.class.uses_active_scaffold? and params[:adapter] and @rendering_adapter.nil?
6
+ @rendering_adapter = true # recursion control
7
+ # if we need an adapter, then we render the actual stuff to a string and insert it into the adapter template
8
+ opts = args.blank? ? Hash.new : args.first
9
+ render :partial => params[:adapter][1..-1],
10
+ :locals => {:payload => render_to_string(opts.merge(:layout => false), &block).html_safe},
11
+ :use_full_path => true, :layout => false
12
+ @rendering_adapter = nil # recursion control
13
+ else
14
+ render_without_active_scaffold(*args, &block)
15
+ end
16
+ end
17
+ alias_method_chain :render, :active_scaffold
18
+
19
+ # Rails 2.x implementation is post-initialization on :active_scaffold method
20
+ end
21
+ end
22
+