active_scaffold 3.0.23 → 3.0.24

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 (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,202 @@
1
+ module ActiveScaffold
2
+ # Provides support for param hashes assumed to be model attributes.
3
+ # Support is primarily needed for creating/editing associated records using a nested hash structure.
4
+ #
5
+ # Paradigm Params Hash (should write unit tests on this):
6
+ # params[:record] = {
7
+ # # a simple record attribute
8
+ # 'name' => 'John',
9
+ # # a plural association hash
10
+ # 'roles' => {
11
+ # # associate with an existing role
12
+ # '5' => {'id' => 5}
13
+ # # associate with an existing role and edit it
14
+ # '6' => {'id' => 6, 'name' => 'designer'}
15
+ # # create and associate a new role
16
+ # '124521' => {'name' => 'marketer'}
17
+ # }
18
+ # # a singular association hash
19
+ # 'location' => {'id' => 12, 'city' => 'New York'}
20
+ # }
21
+ #
22
+ # Simpler association structures are also supported, like:
23
+ # params[:record] = {
24
+ # # a simple record attribute
25
+ # 'name' => 'John',
26
+ # # a plural association ... all ids refer to existing records
27
+ # 'roles' => ['5', '6'],
28
+ # # a singular association ... all ids refer to existing records
29
+ # 'location' => '12'
30
+ # }
31
+ module AttributeParams
32
+ protected
33
+ # Takes attributes (as from params[:record]) and applies them to the parent_record. Also looks for
34
+ # association attributes and attempts to instantiate them as associated objects.
35
+ #
36
+ # This is a secure way to apply params to a record, because it's based on a loop over the columns
37
+ # set. The columns set will not yield unauthorized columns, and it will not yield unregistered columns.
38
+ def update_record_from_params(parent_record, columns, attributes)
39
+ crud_type = parent_record.new_record? ? :create : :update
40
+ return parent_record unless parent_record.authorized_for?(:crud_type => crud_type)
41
+
42
+ multi_parameter_attributes = {}
43
+ attributes.each do |k, v|
44
+ next unless k.include? '('
45
+ column_name = k.split('(').first.to_sym
46
+ multi_parameter_attributes[column_name] ||= []
47
+ multi_parameter_attributes[column_name] << [k, v]
48
+ end
49
+
50
+ columns.each :for => parent_record, :crud_type => crud_type, :flatten => true do |column|
51
+ # Set any passthrough parameters that may be associated with this column (ie, file column "keep" and "temp" attributes)
52
+ unless column.params.empty?
53
+ column.params.each{|p| parent_record.send("#{p}=", attributes[p]) if attributes.has_key? p}
54
+ end
55
+
56
+ if multi_parameter_attributes.has_key? column.name
57
+ parent_record.send(:assign_multiparameter_attributes, multi_parameter_attributes[column.name])
58
+ elsif attributes.has_key? column.name
59
+ value = column_value_from_param_value(parent_record, column, attributes[column.name])
60
+
61
+ # we avoid assigning a value that already exists because otherwise has_one associations will break (AR bug in has_one_association.rb#replace)
62
+ parent_record.send("#{column.name}=", value) unless parent_record.send(column.name) == value
63
+
64
+ elsif column.plural_association?
65
+ parent_record.send("#{column.name}=", [])
66
+ end
67
+ end
68
+
69
+ if parent_record.new_record?
70
+ parent_record.class.reflect_on_all_associations.each do |a|
71
+ next unless [:has_one, :has_many].include?(a.macro) and not (a.options[:through] || a.options[:finder_sql])
72
+ next unless association_proxy = parent_record.send(a.name)
73
+
74
+ raise ActiveScaffold::ReverseAssociationRequired, "Association #{a.name} in class #{parent_record.class.name}: In order to support :has_one and :has_many where the parent record is new and the child record(s) validate the presence of the parent, ActiveScaffold requires the reverse association (the belongs_to)." unless a.reverse
75
+
76
+ association_proxy = [association_proxy] if a.macro == :has_one
77
+ association_proxy.each { |record| record.send("#{a.reverse}=", parent_record) }
78
+ end
79
+ end
80
+
81
+ parent_record
82
+ end
83
+
84
+ def manage_nested_record_from_params(parent_record, column, attributes)
85
+ record = find_or_create_for_params(attributes, column, parent_record)
86
+ if record
87
+ record_columns = active_scaffold_config_for(column.association.klass).subform.columns
88
+ update_record_from_params(record, record_columns, attributes)
89
+ record.unsaved = true
90
+ end
91
+ record
92
+ end
93
+
94
+ def column_value_from_param_value(parent_record, column, value)
95
+ # convert the value, possibly by instantiating associated objects
96
+ if value.is_a?(Hash)
97
+ column_value_from_param_hash_value(parent_record, column, value)
98
+ else
99
+ column_value_from_param_simple_value(parent_record, column, value)
100
+ end
101
+ end
102
+
103
+ def column_value_from_param_simple_value(parent_record, column, value)
104
+ debugger if column.name == :working_days
105
+ if column.singular_association?
106
+ # it's a single id
107
+ column.association.klass.find(value) if value and not value.empty?
108
+ elsif column.plural_association?
109
+ column_plural_assocation_value_from_value(column, value)
110
+ elsif column.number? && [:i18n_number, :currency].include?(column.options[:format])
111
+ self.class.i18n_number_to_native_format(value)
112
+ else
113
+ # convert empty strings into nil. this works better with 'null => true' columns (and validations),
114
+ # and 'null => false' columns should just convert back to an empty string.
115
+ # ... but we can at least check the ConnectionAdapter::Column object to see if nulls are allowed
116
+ value = nil if value.is_a? String and value.empty? and !column.column.nil? and column.column.null
117
+ value
118
+ end
119
+ end
120
+
121
+ def column_plural_assocation_value_from_value(column, value)
122
+ # it's an array of ids
123
+ if value and not value.empty?
124
+ ids = value.select {|id| id.respond_to?(:empty?) ? !id.empty? : true}
125
+ ids.empty? ? [] : column.association.klass.find(ids)
126
+ end
127
+ end
128
+
129
+ def column_value_from_param_hash_value(parent_record, column, value)
130
+ # this is just for backwards compatibility. we should clean this up in 2.0.
131
+ if column.form_ui == :select
132
+ ids = if column.singular_association?
133
+ value[:id]
134
+ else
135
+ value.values.collect {|hash| hash[:id]}
136
+ end
137
+ (ids and not ids.empty?) ? column.association.klass.find(ids) : nil
138
+
139
+ elsif column.singular_association?
140
+ manage_nested_record_from_params(parent_record, column, value)
141
+ elsif column.plural_association?
142
+ value.collect {|key_value_pair| manage_nested_record_from_params(parent_record, column, key_value_pair[1])}.compact
143
+ else
144
+ value
145
+ end
146
+ end
147
+
148
+ # Attempts to create or find an instance of klass (which must be an ActiveRecord object) from the
149
+ # request parameters given. If params[:id] exists it will attempt to find an existing object
150
+ # otherwise it will build a new one.
151
+ def find_or_create_for_params(params, parent_column, parent_record)
152
+ current = parent_record.send(parent_column.name)
153
+ klass = parent_column.association.klass
154
+ pk = klass.primary_key.to_sym
155
+ return nil if parent_column.show_blank_record?(current) and attributes_hash_is_empty?(params, klass)
156
+
157
+ if params.has_key? pk
158
+ # modifying the current object of a singular association
159
+ pk_val = params[pk]
160
+ if current and current.is_a? ActiveRecord::Base and current.id.to_s == pk_val
161
+ current
162
+ # modifying one of the current objects in a plural association
163
+ elsif current and current.respond_to?(:any?) and current.any? {|o| o.id.to_s == pk_val}
164
+ current.detect {|o| o.id.to_s == pk_val}
165
+ # attaching an existing but not-current object
166
+ else
167
+ klass.find(pk_val)
168
+ end
169
+ else
170
+ build_associated(parent_column, parent_record) if klass.authorized_for?(:crud_type => :create)
171
+ end
172
+ end
173
+ # Determines whether the given attributes hash is "empty".
174
+ # This isn't a literal emptiness - it's an attempt to discern whether the user intended it to be empty or not.
175
+ def attributes_hash_is_empty?(hash, klass)
176
+ ignore_column_types = [:boolean]
177
+ hash.all? do |key,value|
178
+ # convert any possible multi-parameter attributes like 'created_at(5i)' to simply 'created_at'
179
+ parts = key.to_s.split('(')
180
+ #old style date form management... ignore them too
181
+ ignore_column_types = [:boolean, :datetime, :date, :time] if parts.length > 1
182
+ column_name = parts.first
183
+ column = klass.columns_hash[column_name]
184
+
185
+ # booleans and datetimes will always have a value. so we ignore them when checking whether the hash is empty.
186
+ # this could be a bad idea. but the current situation (excess record entry) seems worse.
187
+ next true if column and ignore_column_types.include?(column.type)
188
+
189
+ # defaults are pre-filled on the form. we can't use them to determine if the user intends a new row.
190
+ next true if column and value == column.default.to_s
191
+
192
+ if value.is_a?(Hash)
193
+ attributes_hash_is_empty?(value, klass)
194
+ elsif value.is_a?(Array)
195
+ value.any? {|id| id.respond_to?(:empty?) ? !id.empty? : true}
196
+ else
197
+ value.respond_to?(:empty?) ? value.empty? : false
198
+ end
199
+ end
200
+ end
201
+ end
202
+ end
@@ -1,20 +1,9 @@
1
- module ActiveScaffold
2
- module RecordSelectBridge
1
+ class ActiveScaffold::Bridges::RecordSelect
2
+ module Helpers
3
3
  def self.included(base)
4
4
  base.class_eval do
5
5
  include FormColumnHelpers
6
6
  include SearchColumnHelpers
7
- include ViewHelpers
8
- end
9
- end
10
-
11
- module ViewHelpers
12
- def self.included(base)
13
- base.alias_method_chain :active_scaffold_includes, :record_select
14
- end
15
-
16
- def active_scaffold_includes_with_record_select(*args)
17
- active_scaffold_includes_without_record_select(*args) + record_select_includes
18
7
  end
19
8
  end
20
9
 
@@ -50,15 +39,17 @@ module ActiveScaffold
50
39
  record_select_options.merge!(column.options)
51
40
  if options['data-update_url']
52
41
  record_select_options[:onchange] = %|function(id, label) {
53
- ActiveScaffold.update_column($("##{options[:id]}"), "#{options['data-update_url']}", #{options['data-update_send_form'].to_json}, "#{options[:id]}", id);
42
+ ActiveScaffold.update_column(null, "#{options['data-update_url']}", #{options['data-update_send_form'].to_json}, "#{options[:id]}", id);
54
43
  }|
55
44
  end
56
45
 
57
- if multiple
46
+ html = if multiple
58
47
  record_multi_select_field(options[:name], value || [], record_select_options)
59
48
  else
60
49
  record_select_field(options[:name], value || column.association.klass.new, record_select_options)
61
50
  end
51
+ html = self.class.field_error_proc.call(html, self) if @record.errors[column.name].any?
52
+ html
62
53
  end
63
54
  end
64
55
 
@@ -87,4 +78,4 @@ module ActiveScaffold
87
78
  end
88
79
  end
89
80
 
90
- ActionView::Base.class_eval { include ActiveScaffold::RecordSelectBridge }
81
+ ActionView::Base.class_eval { include ActiveScaffold::Bridges::RecordSelect::Helpers }
@@ -0,0 +1,209 @@
1
+ module ActiveScaffold
2
+ module Bridges
3
+ module Shared
4
+ module DateBridge
5
+ module SearchColumnHelpers
6
+ def active_scaffold_search_date_bridge(column, options)
7
+ current_search = {'from' => nil, 'to' => nil, 'opt' => 'BETWEEN',
8
+ 'number' => 1, 'unit' => 'DAYS', 'range' => nil}
9
+ current_search.merge!(options[:value]) unless options[:value].nil?
10
+ tags = []
11
+ tags << active_scaffold_search_date_bridge_comparator_tag(column, options, current_search)
12
+ tags << active_scaffold_search_date_bridge_trend_tag(column, options, current_search)
13
+ tags << active_scaffold_search_date_bridge_numeric_tag(column, options, current_search)
14
+ tags << active_scaffold_search_date_bridge_range_tag(column, options, current_search)
15
+ tags.join("&nbsp;").html_safe
16
+ end
17
+
18
+ def active_scaffold_search_date_bridge_comparator_options(column)
19
+ select_options = ActiveScaffold::Finder::DateComparators.collect {|comp| [as_(comp.downcase.to_sym), comp]}
20
+ select_options + ActiveScaffold::Finder::NumericComparators.collect {|comp| [as_(comp.downcase.to_sym), comp]}
21
+ end
22
+
23
+ def active_scaffold_search_date_bridge_comparator_tag(column, options, current_search)
24
+ select_tag("#{options[:name]}[opt]", options_for_select(active_scaffold_search_date_bridge_comparator_options(column),current_search['opt']), :id => "#{options[:id]}_opt", :class => "as_search_range_option as_search_date_time_option")
25
+ end
26
+
27
+ def active_scaffold_search_date_bridge_numeric_tag(column, options, current_search)
28
+ numeric_controls = "" <<
29
+ active_scaffold_search_date_bridge_calendar_control(column, options, current_search, 'from') <<
30
+ content_tag(:span, (" - " + active_scaffold_search_date_bridge_calendar_control(column, options, current_search, 'to')).html_safe,
31
+ :id => "#{options[:id]}_between", :class => "as_search_range_between", :style => "display:#{current_search['opt'] == 'BETWEEN' ? '' : 'none'}")
32
+ content_tag("span", numeric_controls.html_safe, :id => "#{options[:id]}_numeric", :style => "display:#{ActiveScaffold::Finder::NumericComparators.include?(current_search['opt']) ? '' : 'none'}")
33
+ end
34
+
35
+ def active_scaffold_search_date_bridge_trend_tag(column, options, current_search)
36
+ active_scaffold_date_bridge_trend_tag(column, options,
37
+ {:name_prefix => 'search',
38
+ :number_value => current_search['number'],
39
+ :unit_value => current_search["unit"],
40
+ :show => (current_search['opt'] == 'PAST' || current_search['opt'] == 'FUTURE')})
41
+ end
42
+
43
+ def active_scaffold_date_bridge_trend_tag(column, options, trend_options)
44
+ trend_controls = text_field_tag("#{trend_options[:name_prefix]}[#{column.name}][number]", trend_options[:number_value], :class => 'text-input', :size => 10, :autocomplete => 'off') << " " <<
45
+ select_tag("#{trend_options[:name_prefix]}[#{column.name}][unit]",
46
+ options_for_select(active_scaffold_search_date_bridge_trend_units(column), trend_options[:unit_value]),
47
+ :class => 'text-input')
48
+ content_tag("span", trend_controls.html_safe, :id => "#{options[:id]}_trend", :style => "display:#{trend_options[:show] ? '' : 'none'}")
49
+ end
50
+
51
+ def active_scaffold_search_date_bridge_trend_units(column)
52
+ options = ActiveScaffold::Finder::DateUnits.collect{|unit| [as_(unit.downcase.to_sym), unit]}
53
+ options = ActiveScaffold::Finder::TimeUnits.collect{|unit| [as_(unit.downcase.to_sym), unit]} + options if column_datetime?(column)
54
+ options
55
+ end
56
+
57
+ def active_scaffold_search_date_bridge_range_tag(column, options, current_search)
58
+ range_controls = select_tag("search[#{column.name}][range]",
59
+ options_for_select( ActiveScaffold::Finder::DateRanges.collect{|range| [as_(range.downcase.to_sym), range]}, current_search["range"]),
60
+ :class => 'text-input')
61
+ content_tag("span", range_controls.html_safe, :id => "#{options[:id]}_range", :style => "display:#{(current_search['opt'] == 'RANGE') ? '' : 'none'}")
62
+ end
63
+
64
+ def column_datetime?(column)
65
+ (!column.column.nil? && [:datetime, :time].include?(column.column.type))
66
+ end
67
+ end
68
+
69
+ module HumanConditionHelpers
70
+ def active_scaffold_human_condition_date_bridge(column, value)
71
+ case value[:opt]
72
+ when 'RANGE'
73
+ range_type, range = value[:range].downcase.split('_')
74
+ format = active_scaffold_human_condition_date_bridge_range_format(range_type, range)
75
+ from, to = controller.class.date_bridge_from_to(column, value)
76
+ "#{column.active_record_class.human_attribute_name(column.name)} = #{as_(value[:range].downcase).downcase} (#{I18n.l(from, :format => format)})"
77
+ when 'PAST', 'FUTURE'
78
+ from, to = controller.class.date_bridge_from_to(column, value)
79
+ "#{column.active_record_class.human_attribute_name(column.name)} #{as_('BETWEEN'.downcase).downcase} #{I18n.l(from)} - #{I18n.l(to)}"
80
+ else
81
+ from, to = controller.class.date_bridge_from_to(column, value)
82
+ "#{column.active_record_class.human_attribute_name(column.name)} #{as_(value[:opt].downcase).downcase} #{I18n.l(from)} #{value[:opt] == 'BETWEEN' ? '- ' + I18n.l(to) : ''}"
83
+ end
84
+ end
85
+
86
+ def active_scaffold_human_condition_date_bridge_range_format(range_type, range)
87
+ case range
88
+ when 'week'
89
+ first_day_of_week = I18n.translate 'active_scaffold.date_picker_options.firstDay'
90
+ if first_day_of_week == 1
91
+ '%W %Y'
92
+ else
93
+ '%U %Y'
94
+ end
95
+ when 'month'
96
+ '%b %Y'
97
+ when 'year'
98
+ '%Y'
99
+ else
100
+ I18n.translate 'date.formats.default'
101
+ end
102
+ end
103
+ end
104
+
105
+ module Finder
106
+ module ClassMethods
107
+ def condition_for_date_bridge_type(column, value, like_pattern)
108
+ operator = ActiveScaffold::Finder::NumericComparators.include?(value[:opt]) && value[:opt] != 'BETWEEN' ? value[:opt] : nil
109
+ from_value, to_value = date_bridge_from_to(column, value)
110
+
111
+ if column.search_sql.is_a? Proc
112
+ column.search_sql.call(from_value, to_value, operator)
113
+ else
114
+ unless operator.nil?
115
+ ["#{column.search_sql} #{value[:opt]} ?", from_value.to_s(:db)] unless from_value.nil?
116
+ else
117
+ ["#{column.search_sql} BETWEEN ? AND ?", from_value.to_s(:db), to_value.to_s(:db)] unless from_value.nil? && to_value.nil?
118
+ end
119
+ end
120
+ end
121
+
122
+ def date_bridge_from_to(column, value)
123
+ conversion = column.column.type == :date ? :to_date : :to_time
124
+ case value[:opt]
125
+ when 'RANGE'
126
+ date_bridge_from_to_for_range(column, value).collect(&conversion)
127
+ when 'PAST', 'FUTURE'
128
+ date_bridge_from_to_for_trend(column, value).collect(&conversion)
129
+ else
130
+ ['from', 'to'].collect { |field| condition_value_for_datetime(value[field], conversion)}
131
+ end
132
+ end
133
+
134
+ def date_bridge_now
135
+ Time.zone.now
136
+ end
137
+
138
+ def date_bridge_from_to_for_trend(column, value)
139
+ case value['opt']
140
+ when "PAST"
141
+ trend_number = [value['number'].to_i, 1].max
142
+ now = date_bridge_now
143
+ if date_bridge_column_date?(column)
144
+ from = now.beginning_of_day.ago((trend_number).send(value['unit'].downcase.singularize.to_sym))
145
+ to = now.end_of_day
146
+ else
147
+ from = now.ago((trend_number).send(value['unit'].downcase.singularize.to_sym))
148
+ to = now
149
+ end
150
+ return from, to
151
+ when "FUTURE"
152
+ trend_number = [value['number'].to_i, 1].max
153
+ now = date_bridge_now
154
+ if date_bridge_column_date?(column)
155
+ from = now.beginning_of_day
156
+ to = now.end_of_day.in((trend_number).send(value['unit'].downcase.singularize.to_sym))
157
+ else
158
+ from = now
159
+ to = now.in((trend_number).send(value['unit'].downcase.singularize.to_sym))
160
+ end
161
+ return from, to
162
+ end
163
+ end
164
+
165
+ def date_bridge_from_to_for_range(column, value)
166
+ case value[:range]
167
+ when 'TODAY'
168
+ return date_bridge_now.beginning_of_day, date_bridge_now.end_of_day
169
+ when 'YESTERDAY'
170
+ return date_bridge_now.ago(1.day).beginning_of_day, date_bridge_now.ago(1.day).end_of_day
171
+ when 'TOMORROW'
172
+ return date_bridge_now.in(1.day).beginning_of_day, date_bridge_now.in(1.day).end_of_day
173
+ else
174
+ range_type, range = value[:range].downcase.split('_')
175
+ raise ArgumentError unless ['week', 'month', 'year'].include?(range)
176
+ case range_type
177
+ when 'this'
178
+ return date_bridge_now.send("beginning_of_#{range}".to_sym), date_bridge_now.send("end_of_#{range}")
179
+ when 'prev'
180
+ return date_bridge_now.ago(1.send(range.to_sym)).send("beginning_of_#{range}".to_sym), date_bridge_now.ago(1.send(range.to_sym)).send("end_of_#{range}".to_sym)
181
+ when 'next'
182
+ return date_bridge_now.in(1.send(range.to_sym)).send("beginning_of_#{range}".to_sym), date_bridge_now.in(1.send(range.to_sym)).send("end_of_#{range}".to_sym)
183
+ else
184
+ return nil, nil
185
+ end
186
+ end
187
+ end
188
+
189
+ def date_bridge_column_date?(column)
190
+ if [:date_picker, :datetime_picker].include? column.form_ui
191
+ column.form_ui == :date_picker
192
+ else
193
+ (!column.column.nil? && [:date].include?(column.column.type))
194
+ end
195
+ end
196
+ end
197
+ end
198
+ end
199
+ end
200
+ end
201
+ end
202
+
203
+ ActiveScaffold::Finder.const_set('DateComparators', ["PAST", "FUTURE", "RANGE"])
204
+ ActiveScaffold::Finder.const_set('DateUnits', ["DAYS", "WEEKS", "MONTHS", "YEARS"])
205
+ ActiveScaffold::Finder.const_set('TimeUnits', ["SECONDS", "MINUTES", "HOURS"])
206
+ ActiveScaffold::Finder.const_set('DateRanges', ["TODAY", "YESTERDAY", "TOMORROW",
207
+ "THIS_WEEK", "PREV_WEEK", "NEXT_WEEK",
208
+ "THIS_MONTH", "PREV_MONTH", "NEXT_MONTH",
209
+ "THIS_YEAR", "PREV_YEAR", "NEXT_YEAR"])
@@ -4,7 +4,7 @@ module ActiveScaffold::Config
4
4
  def initialize(*args)
5
5
  super
6
6
  self.persistent = self.class.persistent
7
- self.edit_after_create = self.class.edit_after_create
7
+ self.action_after_create = self.class.action_after_create
8
8
  self.refresh_list = self.class.refresh_list
9
9
  end
10
10
 
@@ -24,8 +24,8 @@ module ActiveScaffold::Config
24
24
  @@persistent = false
25
25
 
26
26
  # whether update form is opened after a create or not
27
- cattr_accessor :edit_after_create
28
- @@edit_after_create = false
27
+ cattr_accessor :action_after_create
28
+ @@action_after_create = false
29
29
 
30
30
  # whether we should refresh list after create or not
31
31
  cattr_accessor :refresh_list
@@ -43,7 +43,7 @@ module ActiveScaffold::Config
43
43
  attr_accessor :persistent
44
44
 
45
45
  # whether the form stays open after a create or not
46
- attr_accessor :edit_after_create
46
+ attr_accessor :action_after_create
47
47
 
48
48
  # whether we should refresh list after create or not
49
49
  attr_accessor :refresh_list
@@ -23,6 +23,7 @@ module ActiveScaffold::Config
23
23
  unless column.nil? || column.association.nil?
24
24
  options.reverse_merge! :security_method => :nested_authorized?, :label => column.association.klass.model_name.human({:count => 2, :default => column.association.klass.name.pluralize})
25
25
  action_link = @core.link_for_association(column, options)
26
+ action_link.action ||= :index
26
27
  @core.action_links.add_to_group(action_link, action_group) unless action_link.nil?
27
28
  else
28
29
 
@@ -0,0 +1,41 @@
1
+ module ActiveScaffold::Config
2
+ class Nested < Base
3
+ self.crud_type = :read
4
+
5
+ def initialize(core_config)
6
+ super
7
+ @label = :add_existing_model
8
+ self.shallow_delete = self.class.shallow_delete
9
+ @action_group = self.class.action_group.clone if self.class.action_group
10
+ end
11
+
12
+ # global level configuration
13
+ # --------------------------
14
+ cattr_accessor :shallow_delete
15
+ @@shallow_delete = true
16
+
17
+ # instance-level configuration
18
+ # ----------------------------
19
+ attr_accessor :shallow_delete
20
+
21
+ # Add a nested ActionLink
22
+ def add_link(attribute, options = {})
23
+ column = @core.columns[attribute.to_sym]
24
+ unless column.nil? || column.association.nil?
25
+ options.reverse_merge! :security_method => :nested_authorized?, :label => column.association.klass.model_name.human({:count => 2, :default => column.association.klass.name.pluralize})
26
+ action_link = @core.link_for_association(column, options)
27
+ @core.action_links.add_to_group(action_link, action_group) unless action_link.nil?
28
+ else
29
+
30
+ end
31
+ end
32
+
33
+ def add_scoped_link(named_scope, options = {})
34
+ action_link = @core.link_for_association_as_scope(named_scope.to_sym, options)
35
+ @core.action_links.add_to_group(action_link, action_group) unless action_link.nil?
36
+ end
37
+
38
+ # the label for this Nested action. used for the header.
39
+ attr_writer :label
40
+ end
41
+ end
@@ -0,0 +1,74 @@
1
+ module ActiveScaffold::Config
2
+ class Search < Base
3
+ self.crud_type = :read
4
+
5
+ def initialize(core_config)
6
+ super
7
+ @text_search = self.class.text_search
8
+ @live = self.class.live?
9
+ @split_terms = self.class.split_terms
10
+
11
+ # start with the ActionLink defined globally
12
+ @link = self.class.link.clone
13
+ @action_group = self.class.action_group.clone if self.class.action_group
14
+ end
15
+
16
+
17
+ # global level configuration
18
+ # --------------------------
19
+ # the ActionLink for this action
20
+ cattr_accessor :link
21
+ @@link = ActiveScaffold::DataStructures::ActionLink.new('show_search', :label => :search, :type => :collection, :security_method => :search_authorized?, :ignore_method => :search_ignore?)
22
+
23
+ # A flag for how the search should do full-text searching in the database:
24
+ # * :full: LIKE %?%
25
+ # * :start: LIKE ?%
26
+ # * :end: LIKE %?
27
+ # * false: LIKE ?
28
+ # Default is :full
29
+ cattr_accessor :text_search
30
+ @@text_search = :full
31
+
32
+ # whether submits the search as you type
33
+ cattr_writer :live
34
+ def self.live?
35
+ @@live
36
+ end
37
+
38
+ # instance-level configuration
39
+ # ----------------------------
40
+
41
+ # provides access to the list of columns specifically meant for the Search to use
42
+ def columns
43
+ # we want to delay initializing to the @core.columns set for as long as possible. Too soon and .search_sql will not be available to .searchable?
44
+ unless @columns
45
+ debugger
46
+ self.columns = @core.columns.collect{|c| c.name if @core.columns._inheritable.include?(c.name) and c.searchable? and c.column and c.column.text?}.compact
47
+ end
48
+ @columns
49
+ end
50
+
51
+ public :columns=
52
+
53
+ # A flag for how the search should do full-text searching in the database:
54
+ # * :full: LIKE %?%
55
+ # * :start: LIKE ?%
56
+ # * :end: LIKE %?
57
+ # * false: LIKE ?
58
+ # Default is :full
59
+ attr_accessor :text_search
60
+
61
+ @@split_terms = " "
62
+ cattr_accessor :split_terms
63
+ attr_accessor :split_terms
64
+
65
+ # the ActionLink for this action
66
+ attr_accessor :link
67
+
68
+ # whether submits the search as you type
69
+ attr_writer :live
70
+ def live?
71
+ @live
72
+ end
73
+ end
74
+ end