interview 0.0.1 → 0.0.6
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.
- checksums.yaml +4 -4
- data/interview.gemspec +4 -0
- data/lib/generators/interview/install/install_generator.rb +47 -0
- data/lib/generators/interview/install/templates/32px.png +0 -0
- data/lib/generators/interview/install/templates/40px.png +0 -0
- data/lib/generators/interview/install/templates/application.js +22 -0
- data/lib/generators/interview/install/templates/bootstrap_interview.css.scss +98 -0
- data/lib/generators/interview/install/templates/ckeditor_config.js +19 -0
- data/lib/generators/interview/install/templates/colors.css.scss +2 -0
- data/lib/generators/interview/install/templates/defaults_de.yml +204 -0
- data/lib/generators/interview/install/templates/interview.js.coffee +72 -0
- data/lib/generators/interview/install/templates/interview.rb +4 -0
- data/lib/generators/interview/install/templates/jstree.css +916 -0
- data/lib/generators/interview/install/templates/jstree.js +5892 -0
- data/lib/generators/interview/install/templates/missing_large.png +0 -0
- data/lib/generators/interview/install/templates/missing_medium.png +0 -0
- data/lib/generators/interview/install/templates/missing_thumb.png +0 -0
- data/lib/generators/interview/install/templates/throbber.gif +0 -0
- data/lib/generators/interview/install/templates/views_defaults_de.yml +8 -0
- data/lib/generators/interview/view_control/templates/card.rb +6 -0
- data/lib/generators/interview/view_control/templates/form.rb +6 -0
- data/lib/generators/interview/view_control/templates/list.rb +6 -0
- data/lib/generators/interview/view_control/view_control_generator.rb +34 -0
- data/lib/interview/actionbar.rb +38 -0
- data/lib/interview/association_attribute.rb +33 -0
- data/lib/interview/association_list_attribute.rb +36 -0
- data/lib/interview/association_methods.rb +23 -0
- data/lib/interview/attribute.rb +141 -0
- data/lib/interview/boolean_attribute.rb +59 -0
- data/lib/interview/breadcrumbs.rb +21 -0
- data/lib/interview/container_attribute.rb +30 -0
- data/lib/interview/control.rb +73 -0
- data/lib/interview/control_def.rb +36 -0
- data/lib/interview/date_attribute.rb +20 -0
- data/lib/interview/datetime_attribute.rb +20 -0
- data/lib/interview/decimal_attribute.rb +10 -0
- data/lib/interview/dropdown.rb +26 -0
- data/lib/interview/form.rb +69 -0
- data/lib/interview/form_errors.rb +22 -0
- data/lib/interview/grid.rb +53 -0
- data/lib/interview/has_controls.rb +43 -0
- data/lib/interview/hidden_attribute.rb +15 -0
- data/lib/interview/html_control.rb +15 -0
- data/lib/interview/html_text_attribute.rb +19 -0
- data/lib/interview/image_attribute.rb +34 -0
- data/lib/interview/integer_attribute.rb +5 -0
- data/lib/interview/link.rb +129 -0
- data/lib/interview/list.rb +21 -0
- data/lib/interview/navigation.rb +55 -0
- data/lib/interview/navigation_item.rb +26 -0
- data/lib/interview/nested_form.rb +51 -0
- data/lib/interview/nested_form_add_link.rb +80 -0
- data/lib/interview/nested_form_remove_link.rb +28 -0
- data/lib/interview/object_context.rb +17 -0
- data/lib/interview/option_attribute.rb +74 -0
- data/lib/interview/polymorphic_add_link.rb +28 -0
- data/lib/interview/space.rb +18 -0
- data/lib/interview/string_attribute.rb +5 -0
- data/lib/interview/tab.rb +21 -0
- data/lib/interview/tab_box.rb +29 -0
- data/lib/interview/text.rb +15 -0
- data/lib/interview/text_attribute.rb +21 -0
- data/lib/interview/tooltip.rb +38 -0
- data/lib/interview/tree.rb +33 -0
- data/lib/interview/version.rb +1 -1
- data/lib/interview/view.rb +87 -0
- data/lib/interview.rb +196 -0
- metadata +107 -2
@@ -0,0 +1,17 @@
|
|
1
|
+
module Interview
|
2
|
+
module ObjectContext # todo: Überdenken
|
3
|
+
|
4
|
+
attr_reader :object
|
5
|
+
|
6
|
+
def object=(object)
|
7
|
+
if object.is_a? String
|
8
|
+
@object = Object::const_get(object.camelcase).new
|
9
|
+
elsif object.is_a? Class
|
10
|
+
@object = object.new
|
11
|
+
else
|
12
|
+
@object = object
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module Interview
|
2
|
+
class OptionAttribute < Attribute
|
3
|
+
|
4
|
+
attr_accessor :html_class, :use_radios
|
5
|
+
|
6
|
+
def render_read
|
7
|
+
return '' if value.nil? or value == ''
|
8
|
+
object = find_attribute! :object
|
9
|
+
if object.class.superclass.name == 'ActiveRecord::Base'
|
10
|
+
model = object.class.name.underscore
|
11
|
+
else
|
12
|
+
model = object.class.superclass.name.underscore
|
13
|
+
end
|
14
|
+
return h.t "activerecord.options.#{model}.#{@method}.#{value}"
|
15
|
+
end
|
16
|
+
|
17
|
+
def render_write
|
18
|
+
if @use_radios
|
19
|
+
render_radios
|
20
|
+
else
|
21
|
+
render_select
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def render_select
|
26
|
+
object = find_attribute!(:object)
|
27
|
+
options = [[h.t('helpers.select.prompt'), nil]]
|
28
|
+
options += get_options(object)
|
29
|
+
|
30
|
+
html_class = 'form-control'
|
31
|
+
html_class += " #{@html_class}" if @html_class
|
32
|
+
form_builder.select @method, options, {}, {class: html_class}
|
33
|
+
end
|
34
|
+
|
35
|
+
def render_radios
|
36
|
+
object = find_attribute!(:object)
|
37
|
+
html = Builder::XmlMarkup.new
|
38
|
+
get_options(object).each do |option|
|
39
|
+
html.div class: 'radio' do
|
40
|
+
html.label do
|
41
|
+
html << form_builder.radio_button(@method, option[1])
|
42
|
+
html.text! ' '
|
43
|
+
html.text! option[0]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
return html.target!
|
48
|
+
end
|
49
|
+
|
50
|
+
def get_options(object)
|
51
|
+
# todo: nach ActiveModel Translation auslagern
|
52
|
+
options = []
|
53
|
+
general_defaults = object.class.lookup_ancestors.map do |klass|
|
54
|
+
"#{object.class.i18n_scope}.options.#{klass.model_name.i18n_key}.#{method}"
|
55
|
+
end
|
56
|
+
if object.class.const_defined? "#{@method.upcase}_OPTIONS"
|
57
|
+
opts = object.class.const_get("#{@method.upcase}_OPTIONS")
|
58
|
+
elsif object.class.respond_to? :descendants and
|
59
|
+
(klass = object.class.descendants.find { |c| c.const_defined? "#{@method.upcase}_OPTIONS" })
|
60
|
+
opts = klass.const_get("#{@method.upcase}_OPTIONS")
|
61
|
+
end
|
62
|
+
if opts
|
63
|
+
opts.map do |option|
|
64
|
+
defaults = general_defaults.map do |default|
|
65
|
+
:"#{default}.#{option}"
|
66
|
+
end
|
67
|
+
options << [h.t(defaults.shift, default: defaults), option]
|
68
|
+
end
|
69
|
+
end
|
70
|
+
return options
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Interview
|
2
|
+
class PolymorphicAddLink < Control
|
3
|
+
|
4
|
+
attr_accessor :polymorphic_classes
|
5
|
+
|
6
|
+
def render
|
7
|
+
poly_classes = @polymorphic_classes.map do |poly_class|
|
8
|
+
poly_class.is_a?(String) ? poly_class.camelcase.constantize : poly_class
|
9
|
+
end
|
10
|
+
|
11
|
+
select_options = [[ h.t('helpers.select.prompt'), nil ]]
|
12
|
+
select_options += poly_classes.map do |poly_class|
|
13
|
+
[ poly_class.model_name.human, poly_class.name ]
|
14
|
+
end
|
15
|
+
|
16
|
+
html = Builder::XmlMarkup.new
|
17
|
+
html.div do
|
18
|
+
html << h.select_tag("add_link_class", h.options_for_select(select_options), class: 'form-control', style: 'display: inline; width: auto;')
|
19
|
+
html.text! ' '
|
20
|
+
link = Link.new parent: self, caption: h.t('views.add'), action: 'new', style: 'button'
|
21
|
+
link.html_class << 'polymorphic_add_link'
|
22
|
+
html << link.render
|
23
|
+
end
|
24
|
+
return html.target!
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Interview
|
2
|
+
class Space < Control
|
3
|
+
attr_accessor :style
|
4
|
+
|
5
|
+
def render
|
6
|
+
html = Builder::XmlMarkup.new
|
7
|
+
if @style and @style.to_sym == :line
|
8
|
+
html.hr
|
9
|
+
elsif @style and @style.to_sym == :break
|
10
|
+
html.br
|
11
|
+
else
|
12
|
+
html.text! ' '
|
13
|
+
end
|
14
|
+
return html.target!
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Interview
|
2
|
+
class Tab < Control
|
3
|
+
|
4
|
+
include HasControls
|
5
|
+
|
6
|
+
attr_accessor :caption, :active
|
7
|
+
|
8
|
+
def render
|
9
|
+
html = Builder::XmlMarkup.new
|
10
|
+
html_class = 'tab-pane'
|
11
|
+
html_class += ' active' if @active
|
12
|
+
html.div class: html_class, id: @caption do
|
13
|
+
@controls.each do |control|
|
14
|
+
html << control.render
|
15
|
+
end
|
16
|
+
end
|
17
|
+
return html.target!
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Interview
|
2
|
+
class TabBox < Control
|
3
|
+
|
4
|
+
include HasControls
|
5
|
+
|
6
|
+
def render
|
7
|
+
if @controls.none? { |c| c.active }
|
8
|
+
@controls.first.active = true
|
9
|
+
end
|
10
|
+
|
11
|
+
html = Builder::XmlMarkup.new
|
12
|
+
html.ul class: 'nav nav-tabs tab_box' do
|
13
|
+
@controls.each do |control|
|
14
|
+
html_class = control.active ? 'active' : ''
|
15
|
+
html.li class: html_class do
|
16
|
+
html.a control.caption, href: "##{control.caption}", :'data-toogle' => 'tab'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
html.div class: 'tab-content' do
|
21
|
+
@controls.each do |control|
|
22
|
+
html << control.render
|
23
|
+
end
|
24
|
+
end
|
25
|
+
return html.target!
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Interview
|
2
|
+
class TextAttribute < Attribute
|
3
|
+
|
4
|
+
attr_accessor :line_break, :cut, :rows
|
5
|
+
|
6
|
+
def render_read
|
7
|
+
return '' if value.nil?
|
8
|
+
line_break = @line_break || true
|
9
|
+
|
10
|
+
value = self.value.gsub(/\n/, '<br/>') if line_break
|
11
|
+
value = value[0..29] + '...' if @cut and value.size > 30
|
12
|
+
return value.html_safe
|
13
|
+
end
|
14
|
+
|
15
|
+
def render_write
|
16
|
+
rows = @rows || 8
|
17
|
+
form_builder.text_area @method, class: 'form-control', rows: rows
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Interview
|
2
|
+
class Tooltip < Control
|
3
|
+
|
4
|
+
attr_accessor :tooltip, :style
|
5
|
+
|
6
|
+
def tooltip
|
7
|
+
@tooltip || find_attribute(:tooltip)
|
8
|
+
end
|
9
|
+
|
10
|
+
def render
|
11
|
+
if @style and respond_to?("render_#{@style}_style", true)
|
12
|
+
return send "render_#{@style}_style"
|
13
|
+
else
|
14
|
+
return render_default_style
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
protected
|
19
|
+
|
20
|
+
def render_default_style
|
21
|
+
return "<p class='text-muted'>#{tooltip}</p>".html_safe
|
22
|
+
end
|
23
|
+
|
24
|
+
def render_box_style
|
25
|
+
return "<div class='alert alert-info'>#{tooltip}</div>".html_safe
|
26
|
+
end
|
27
|
+
|
28
|
+
def render_popover_style
|
29
|
+
html = Builder::XmlMarkup.new
|
30
|
+
html.text! ' '
|
31
|
+
html.a href: '#', class: 'tip', :'data-toggle' => 'popover', :'data-content' => tooltip do
|
32
|
+
html.span '', class: 'glyphicon glyphicon-question-sign'
|
33
|
+
end
|
34
|
+
return html.target!
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Interview
|
2
|
+
class Tree < Control
|
3
|
+
|
4
|
+
attr_accessor :sortable, :icon
|
5
|
+
|
6
|
+
def render
|
7
|
+
defaults = {}
|
8
|
+
defaults[:icon] = @icon if @icon and @icon != :polymorphic
|
9
|
+
objects = find_attribute! :objects
|
10
|
+
data_content = objects.map do |object|
|
11
|
+
object_data = {
|
12
|
+
id: object.id.to_s,
|
13
|
+
parent: object.parent_id ? object.parent_id.to_s : '#',
|
14
|
+
text: object.human_id,
|
15
|
+
state: {
|
16
|
+
opened: true
|
17
|
+
},
|
18
|
+
a_attr: {
|
19
|
+
href: h.polymorphic_path(object)
|
20
|
+
}
|
21
|
+
}.merge defaults
|
22
|
+
object_data[:icon] = object.icon if @icon == :polymorphic
|
23
|
+
object_data
|
24
|
+
end
|
25
|
+
data = { content: CGI::escapeHTML(data_content.to_json) }
|
26
|
+
data[:sortable] = true if @sortable
|
27
|
+
|
28
|
+
return h.content_tag(:div, '', { class: 'jstree', data: data }, false)
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
data/lib/interview/version.rb
CHANGED
@@ -0,0 +1,87 @@
|
|
1
|
+
module Interview
|
2
|
+
class View < Control
|
3
|
+
include HasControls
|
4
|
+
|
5
|
+
attr_accessor :object, :objects, :filter, :scope, :title, :auto_title_for
|
6
|
+
attr_reader :assoc_object, :assoc_method
|
7
|
+
|
8
|
+
def nested_form
|
9
|
+
return siblings.find { |sib| sib.is_a? Interview::NestedForm }
|
10
|
+
end
|
11
|
+
|
12
|
+
def title
|
13
|
+
if @title # todo: Benötige ich den Titel?
|
14
|
+
return @title
|
15
|
+
elsif @auto_title_for
|
16
|
+
return case @auto_title_for.to_sym
|
17
|
+
when :collection
|
18
|
+
if @assoc_object
|
19
|
+
@assoc_object.class.human_attribute_name(@assoc_method)
|
20
|
+
else
|
21
|
+
@object.class.model_name.human(count: 2)
|
22
|
+
end
|
23
|
+
when :object
|
24
|
+
@object.human_id
|
25
|
+
when :form
|
26
|
+
text = @object.created_at ? 'views.edit' : 'views.new'
|
27
|
+
h.t(text, model: @object.class.model_name.human )
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def singular_title # todo: Überdenken
|
33
|
+
if @auto_title_for
|
34
|
+
if @assoc_object
|
35
|
+
return @assoc_object.class.human_attribute_name(@assoc_method.to_s.singularize)
|
36
|
+
else
|
37
|
+
return @object.class.model_name.human
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def tooltip
|
43
|
+
return h.t("activerecord.tooltips.#{@object.class.name.underscore}.model", default: '')
|
44
|
+
end
|
45
|
+
|
46
|
+
def render
|
47
|
+
if @object.is_a? String
|
48
|
+
# if Object::const_defined?(@object.camelcase) # todo: Überdenken!
|
49
|
+
# @object = @object.camelcase.constantize.new
|
50
|
+
# else
|
51
|
+
@assoc_object = find_attribute! :object
|
52
|
+
@object = @object.split('.').inject(@assoc_object, :send)
|
53
|
+
# end
|
54
|
+
elsif @object.is_a? Class
|
55
|
+
@object = @object.new
|
56
|
+
end
|
57
|
+
|
58
|
+
if @objects.is_a? String
|
59
|
+
@assoc_object = find_attribute! :object
|
60
|
+
@assoc_method = @objects
|
61
|
+
|
62
|
+
@objects = @assoc_object.send @assoc_method
|
63
|
+
@objects = @objects.send @scope if @scope
|
64
|
+
@object = @objects.klass.new
|
65
|
+
@scope = nil
|
66
|
+
@filter = { "#{assoc_object.class.name.underscore}_id" => assoc_object.id }
|
67
|
+
end
|
68
|
+
|
69
|
+
if Object.const_defined? 'Gretel' and parent.nil?
|
70
|
+
model_name = @object.class.lookup_ancestors.last.model_name
|
71
|
+
case h.action_name
|
72
|
+
when 'index'
|
73
|
+
h.breadcrumb model_name.plural.to_sym
|
74
|
+
when 'show'
|
75
|
+
h.breadcrumb model_name.singular.to_sym, @object
|
76
|
+
when 'new'
|
77
|
+
h.breadcrumb "new_#{model_name.singular}".to_sym, @object
|
78
|
+
when 'edit'
|
79
|
+
h.breadcrumb "edit_#{model_name.singular}".to_sym, @object
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
return @controls.map { |c| c.render }.join
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|
data/lib/interview.rb
CHANGED
@@ -1,5 +1,201 @@
|
|
1
|
+
require "active_support/inflector"
|
2
|
+
require "draper"
|
3
|
+
require "builder"
|
4
|
+
|
1
5
|
require "interview/version"
|
2
6
|
|
7
|
+
require "interview/control_def"
|
8
|
+
require "interview/control"
|
9
|
+
require "interview/has_controls"
|
10
|
+
require "interview/object_context"
|
11
|
+
|
12
|
+
require "interview/text"
|
13
|
+
require "interview/link"
|
14
|
+
require "interview/dropdown"
|
15
|
+
require "interview/space"
|
16
|
+
require "interview/tooltip"
|
17
|
+
require "interview/html_control"
|
18
|
+
require "interview/actionbar"
|
19
|
+
require "interview/grid"
|
20
|
+
require "interview/list"
|
21
|
+
require "interview/tree"
|
22
|
+
require "interview/navigation"
|
23
|
+
require "interview/navigation_item"
|
24
|
+
require "interview/tab_box"
|
25
|
+
require "interview/tab"
|
26
|
+
require "interview/breadcrumbs"
|
27
|
+
|
28
|
+
require "interview/attribute"
|
29
|
+
require "interview/string_attribute"
|
30
|
+
require "interview/text_attribute"
|
31
|
+
require "interview/integer_attribute"
|
32
|
+
require "interview/decimal_attribute"
|
33
|
+
require "interview/boolean_attribute"
|
34
|
+
require "interview/date_attribute"
|
35
|
+
require "interview/datetime_attribute"
|
36
|
+
require "interview/option_attribute"
|
37
|
+
require "interview/html_text_attribute"
|
38
|
+
require "interview/image_attribute"
|
39
|
+
require "interview/association_attribute"
|
40
|
+
require "interview/association_list_attribute"
|
41
|
+
require "interview/hidden_attribute"
|
42
|
+
require "interview/container_attribute"
|
43
|
+
|
44
|
+
require "interview/view"
|
45
|
+
require "interview/form"
|
46
|
+
require "interview/nested_form"
|
47
|
+
require "interview/nested_form_remove_link"
|
48
|
+
require "interview/nested_form_add_link"
|
49
|
+
require "interview/form_errors"
|
50
|
+
require "interview/polymorphic_add_link"
|
51
|
+
|
52
|
+
require "interview/association_methods"
|
53
|
+
|
3
54
|
module Interview
|
4
55
|
# Your code goes here...
|
5
56
|
end
|
57
|
+
|
58
|
+
module ActiveRecord
|
59
|
+
class Base
|
60
|
+
|
61
|
+
before_save :set_human_id
|
62
|
+
|
63
|
+
def set_human_id
|
64
|
+
if respond_to? :human_id
|
65
|
+
if respond_to? :name
|
66
|
+
self.human_id = self.name.to_s
|
67
|
+
elsif respond_to? :title
|
68
|
+
self.human_id = self.title.to_s
|
69
|
+
elsif respond_to? :description
|
70
|
+
self.human_id = self.description.to_s
|
71
|
+
else
|
72
|
+
self.human_id = self.id.to_s
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.human_ids
|
78
|
+
objects = select("\"#{self.table_name}\".\"human_id\"").order(:human_id)
|
79
|
+
return objects.map { |object| object.human_id }
|
80
|
+
end
|
81
|
+
|
82
|
+
# todo: auslagern in eigenes gem
|
83
|
+
def self.filter(filter)
|
84
|
+
relation = self
|
85
|
+
self.joins_for_filter(filter).each do |join_table|
|
86
|
+
relation = relation.joins(join_table)
|
87
|
+
end
|
88
|
+
return relation.where(*self.filter_to_sql(filter))
|
89
|
+
end
|
90
|
+
|
91
|
+
def self.joins_for_filter(filter)
|
92
|
+
join_tables = []
|
93
|
+
filter.each do |attr, filter|
|
94
|
+
if assoc = self.reflect_on_association(attr) and assoc.macro != :belongs_to
|
95
|
+
join_tables << assoc.plural_name.to_sym # todo
|
96
|
+
end
|
97
|
+
end
|
98
|
+
return join_tables.uniq
|
99
|
+
end
|
100
|
+
|
101
|
+
def self.filter_to_sql(filter)
|
102
|
+
sqls = []
|
103
|
+
values = []
|
104
|
+
filter.each do |attr, filter|
|
105
|
+
if self.reflect_on_association(attr)
|
106
|
+
result = self.assoc_filter_to_sql(attr, filter)
|
107
|
+
elsif column = self.columns_hash[attr.to_s]
|
108
|
+
result = case column.type
|
109
|
+
when :string
|
110
|
+
self.string_filter_to_sql(attr, filter)
|
111
|
+
when :integer, :decimal
|
112
|
+
self.number_filter_to_sql(attr, filter)
|
113
|
+
when :date
|
114
|
+
self.date_filter_to_sql(attr, filter)
|
115
|
+
when :boolean
|
116
|
+
self.boolean_filter_to_sql(attr, filter)
|
117
|
+
end
|
118
|
+
# todo: erweitern
|
119
|
+
else
|
120
|
+
raise "Not possible to set a filter for #{attr}"
|
121
|
+
end
|
122
|
+
sqls << result.first
|
123
|
+
values += result[1..-1]
|
124
|
+
end
|
125
|
+
sql = sqls.join(' AND ')
|
126
|
+
return [sql] + values
|
127
|
+
end
|
128
|
+
|
129
|
+
def self.string_filter_to_sql(attr, filter)
|
130
|
+
if filter.respond_to?(:each)
|
131
|
+
sql = "#{attr} in (?)"
|
132
|
+
else
|
133
|
+
sql = "#{attr} = ?"
|
134
|
+
end
|
135
|
+
return [sql, filter]
|
136
|
+
end
|
137
|
+
|
138
|
+
def self.number_filter_to_sql(attr, filter)
|
139
|
+
if filter.respond_to?(:each) and filter.none? { |f| f.is_a? String }
|
140
|
+
sql = "#{attr} in (?)"
|
141
|
+
values = [filter]
|
142
|
+
elsif filter.respond_to?(:each)
|
143
|
+
sqls = []
|
144
|
+
values = []
|
145
|
+
filter.each do |f|
|
146
|
+
result = self.number_filter_to_sql(attr, f)
|
147
|
+
sqls << result.first
|
148
|
+
values += results[1..-1]
|
149
|
+
end
|
150
|
+
sql = sqls.join(' OR ')
|
151
|
+
elsif filter.is_a? String
|
152
|
+
unless result = filter.match(/^([<>=]*)(\d+\.?\d*)$/)
|
153
|
+
raise "invalid number filter: '#{filter}'"
|
154
|
+
end
|
155
|
+
operator = result[1] != '' ? result[1] : '='
|
156
|
+
value = result[2].include?('.') ? result[2].to_f : result[2].to_i
|
157
|
+
sql = "#{attr} #{operator} ?"
|
158
|
+
values = [value]
|
159
|
+
else
|
160
|
+
sql = "#{attr} = ?"
|
161
|
+
values = [filter]
|
162
|
+
end
|
163
|
+
return [sql] + values
|
164
|
+
end
|
165
|
+
|
166
|
+
def self.date_filter_to_sql(attr, filter)
|
167
|
+
if filter.respond_to?(:each)
|
168
|
+
if filter.size != 2
|
169
|
+
raise "invalid date filter: #{filter.inspect} 2 elements expected, #{filter.size} given"
|
170
|
+
end
|
171
|
+
sql = "#{attr} BETWEEN ? AND ?"
|
172
|
+
values = filter
|
173
|
+
else
|
174
|
+
sql = "#{attr} = ?"
|
175
|
+
values = [filter]
|
176
|
+
end
|
177
|
+
return [sql] + values
|
178
|
+
end
|
179
|
+
|
180
|
+
def self.boolean_filter_to_sql(attr, filter)
|
181
|
+
return ["#{attr} = ?", filter]
|
182
|
+
end
|
183
|
+
|
184
|
+
def self.assoc_filter_to_sql(attr, filter)
|
185
|
+
assoc = self.reflect_on_association(attr)
|
186
|
+
if assoc.macro == :belongs_to
|
187
|
+
attr = "#{attr}_id"
|
188
|
+
else
|
189
|
+
attr = "#{assoc.plural_name}.id" # todo
|
190
|
+
end
|
191
|
+
return self.number_filter_to_sql(attr, filter)
|
192
|
+
end
|
193
|
+
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
module ActiveRecord::Associations::Builder
|
198
|
+
class CollectionAssociation < Association
|
199
|
+
include AssociationMethods
|
200
|
+
end
|
201
|
+
end
|