admin_it 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (108) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +5 -0
  5. data/.yardopts +3 -0
  6. data/Gemfile +5 -0
  7. data/README.md +58 -0
  8. data/README_RU.md +52 -0
  9. data/Rakefile +23 -0
  10. data/admin_it.gemspec +38 -0
  11. data/app/assets/fonts/admin_it/FontAwesome.otf +0 -0
  12. data/app/assets/fonts/admin_it/fontawesome-webfont.eot +0 -0
  13. data/app/assets/fonts/admin_it/fontawesome-webfont.svg +414 -0
  14. data/app/assets/fonts/admin_it/fontawesome-webfont.ttf +0 -0
  15. data/app/assets/fonts/admin_it/fontawesome-webfont.woff +0 -0
  16. data/app/assets/fonts/admin_it/glyphicons-halflings-regular.eot +0 -0
  17. data/app/assets/fonts/admin_it/glyphicons-halflings-regular.svg +229 -0
  18. data/app/assets/fonts/admin_it/glyphicons-halflings-regular.ttf +0 -0
  19. data/app/assets/fonts/admin_it/glyphicons-halflings-regular.woff +0 -0
  20. data/app/assets/javascript/admin_it/admin_it.js +89 -0
  21. data/app/assets/javascript/admin_it/bootstrap.min.js +6 -0
  22. data/app/assets/stylesheets/admin_it/admin_it.css +25 -0
  23. data/app/assets/stylesheets/admin_it/bootstrap-theme.min.css +7 -0
  24. data/app/assets/stylesheets/admin_it/bootstrap.min.css +7 -0
  25. data/app/assets/stylesheets/admin_it/font-awesome.min.css +4 -0
  26. data/app/views/admin_it/context/_table.html.slim +38 -0
  27. data/app/views/admin_it/context/_tiles.html.slim +23 -0
  28. data/app/views/admin_it/edit.html.slim +2 -0
  29. data/app/views/admin_it/new.html.slim +2 -0
  30. data/app/views/admin_it/shared/_child.html.slim +15 -0
  31. data/app/views/admin_it/shared/_fields.html.slim +19 -0
  32. data/app/views/admin_it/shared/_filters.html.slim +40 -0
  33. data/app/views/admin_it/shared/_form.html.slim +50 -0
  34. data/app/views/admin_it/shared/_pagination.html.slim +41 -0
  35. data/app/views/admin_it/shared/_toolbar.html.slim +30 -0
  36. data/app/views/admin_it/show.html.slim +6 -0
  37. data/app/views/admin_it/table.html.slim +2 -0
  38. data/app/views/admin_it/tiles.html.slim +2 -0
  39. data/app/views/layouts/admin_it.html.slim +37 -0
  40. data/app/views/layouts/admin_it_dialog.html.slim +8 -0
  41. data/config.ru +7 -0
  42. data/lib/admin_it/config.rb +24 -0
  43. data/lib/admin_it/context/collection_context.rb +239 -0
  44. data/lib/admin_it/context/context.rb +232 -0
  45. data/lib/admin_it/context/show_context.rb +45 -0
  46. data/lib/admin_it/context/single_context.rb +199 -0
  47. data/lib/admin_it/context/table_context.rb +66 -0
  48. data/lib/admin_it/context/tiles_context.rb +31 -0
  49. data/lib/admin_it/context.rb +6 -0
  50. data/lib/admin_it/controller.rb +42 -0
  51. data/lib/admin_it/data/active_record.rb +283 -0
  52. data/lib/admin_it/data/data_behavior.rb +18 -0
  53. data/lib/admin_it/data/hash.rb +27 -0
  54. data/lib/admin_it/data/object.rb +51 -0
  55. data/lib/admin_it/data.rb +24 -0
  56. data/lib/admin_it/definitions.rb +89 -0
  57. data/lib/admin_it/engine.rb +56 -0
  58. data/lib/admin_it/env.rb +37 -0
  59. data/lib/admin_it/errors.rb +5 -0
  60. data/lib/admin_it/field/field.rb +174 -0
  61. data/lib/admin_it/field.rb +1 -0
  62. data/lib/admin_it/filters/entity_filter.rb +4 -0
  63. data/lib/admin_it/filters/field_filter.rb +31 -0
  64. data/lib/admin_it/filters/filter.rb +150 -0
  65. data/lib/admin_it/filters/value_filter.rb +52 -0
  66. data/lib/admin_it/filters.rb +4 -0
  67. data/lib/admin_it/helpers/field.rb +25 -0
  68. data/lib/admin_it/helpers/input.rb +22 -0
  69. data/lib/admin_it/helpers/page.rb +26 -0
  70. data/lib/admin_it/helpers/table.rb +85 -0
  71. data/lib/admin_it/helpers/tiles.rb +17 -0
  72. data/lib/admin_it/helpers/toolbar.rb +40 -0
  73. data/lib/admin_it/helpers/top_menu.rb +39 -0
  74. data/lib/admin_it/helpers.rb +12 -0
  75. data/lib/admin_it/locales/en.yml +14 -0
  76. data/lib/admin_it/locales/ru.yml +14 -0
  77. data/lib/admin_it/renderable.rb +18 -0
  78. data/lib/admin_it/resource.rb +292 -0
  79. data/lib/admin_it/utils.rb +21 -0
  80. data/lib/admin_it/version.rb +5 -0
  81. data/lib/admin_it.rb +19 -0
  82. data/lib/extend_it/array_of.rb +181 -0
  83. data/lib/extend_it/asserts.rb +38 -0
  84. data/lib/extend_it/callbacks.rb +105 -0
  85. data/lib/extend_it/caller.rb +35 -0
  86. data/lib/extend_it/class.rb +47 -0
  87. data/lib/extend_it/dsl.rb +94 -0
  88. data/lib/extend_it/refines.rb +6 -0
  89. data/lib/extend_it/symbolize.rb +39 -0
  90. data/lib/extend_it.rb +5 -0
  91. data/spec/internal/config/database.yml +3 -0
  92. data/spec/internal/config/routes.rb +3 -0
  93. data/spec/internal/db/combustion_test.sqlite +0 -0
  94. data/spec/internal/db/schema.rb +3 -0
  95. data/spec/internal/log/.gitignore +1 -0
  96. data/spec/internal/public/favicon.ico +0 -0
  97. data/spec/lib/context/collection_context_spec.rb +20 -0
  98. data/spec/lib/context/context_spec.rb +28 -0
  99. data/spec/lib/context/single_context_spec.rb +26 -0
  100. data/spec/lib/data/object_spec.rb +29 -0
  101. data/spec/lib/definitions_spec.rb +171 -0
  102. data/spec/lib/field_spec.rb +62 -0
  103. data/spec/lib/resource_spec.rb +39 -0
  104. data/spec/lib/utils_spec.rb +39 -0
  105. data/spec/spec_helper.rb +21 -0
  106. data/spec/support/example_groups/context_example_group.rb +50 -0
  107. data/spec/support/shared_examples/context.rb +2 -0
  108. metadata +337 -0
@@ -0,0 +1,51 @@
1
+ module AdminIt
2
+ module ObjectData
3
+ module Context
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ end
7
+
8
+ module ClassMethods
9
+ def load_fields
10
+ all = entity_class.instance_methods(false) - Object.instance_methods
11
+ getters = all.select do |m|
12
+ m.to_s =~ /\w\z/ && entity_class.instance_method(m).arity == 0
13
+ end
14
+ setters = all.select do |m|
15
+ m.to_s[-1] == '=' && entity_class.instance_method(m).arity == 1
16
+ end
17
+ fields = getters.map do |m|
18
+ AdminIt::Field.create(
19
+ m,
20
+ entity_class,
21
+ readable: true,
22
+ writable: setters.include?("#{m}=".to_sym)
23
+ )
24
+ end
25
+ setters.reject! { |m| getters.include?(m.to_s[0..-2].to_sym) }
26
+ fields.concat(setters.map do |m|
27
+ name = m.to_s[0..-2].to_sym
28
+ AdminIt::Field.create(
29
+ name,
30
+ entity_class,
31
+ readable: false,
32
+ writable: true
33
+ )
34
+ end)
35
+ end
36
+ end
37
+ end
38
+
39
+ module Field
40
+ protected
41
+
42
+ def read_value(entity)
43
+ entity.send(name)
44
+ end
45
+
46
+ def write_value(entity, value)
47
+ entity.send("#{name}=", value)
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,24 @@
1
+ require File.join %w(admin_it data data_behavior)
2
+ require File.join %w(admin_it data object)
3
+ require File.join %w(admin_it data hash)
4
+ require File.join %w(admin_it data active_record)
5
+
6
+ module AdminIt
7
+ def self.register_data(entity_class, mod)
8
+ return if entity_class.nil?
9
+ @data_modules ||= []
10
+ @data_modules.unshift [entity_class, mod]
11
+ end
12
+
13
+ def self.data_module(entity_class)
14
+ return nil if entity_class.nil?
15
+ @data_modules ||= []
16
+ @data_modules.each do |mod|
17
+ return mod[1] if entity_class <= mod[0]
18
+ end
19
+ end
20
+
21
+ register_data Object, AdminIt::ObjectData
22
+ register_data Hash, AdminIt::HashData
23
+ register_data ActiveRecord::Base, AdminIt::ActiveRecordData
24
+ end
@@ -0,0 +1,89 @@
1
+ #require 'delegate'
2
+
3
+ module AdminIt
4
+ class ResourceDefinition # < SimpleDelegator
5
+ COLLECTIONS = %i(table tiles list)
6
+ SINGLE = %i(show new edit)
7
+ CONTEXTS = COLLECTIONS + SINGLE
8
+
9
+ # extend ExtendIt::ArrayOf
10
+
11
+ # array_of Context do
12
+ # scope(:all) { |_| true }
13
+ # find_by :name
14
+ # end
15
+
16
+ # dsl_array :context, ArrayOfContext, create_entity: :create_context
17
+
18
+ def initialize(resource)
19
+ @resource = resource
20
+ @resource.contexts.replace(CONTEXTS.map do |name|
21
+ Object.const_get("AdminIt::#{name.capitalize}Context")
22
+ .create_class(name, @resource)
23
+ end)
24
+ end
25
+
26
+ def exclude_context(*args)
27
+ args.flatten.each do |arg|
28
+ arg = Utils.assert_symbol_arg(arg) { next }
29
+ @resource.contexts.reject! { |c| c.context_name == arg }
30
+ end
31
+ end
32
+
33
+ def exclude_collection
34
+ @resource.contexts.reject! { |c| c.collection? }
35
+ end
36
+
37
+ def exclude_single
38
+ @resource.contexts.reject! { |c| c.single? }
39
+ end
40
+
41
+ def contexts(*names, &block)
42
+ unless names.empty?
43
+ @resource.contexts.replace(
44
+ names.flatten.uniq.map { |name| context(name) }
45
+ )
46
+ end
47
+ if block_given?
48
+ @resource.contexts.each { |context| context.instance_eval(&block) }
49
+ end
50
+ end
51
+
52
+ def context(name, context_class: nil, &block)
53
+ Utils.assert_symbol_arg!(name, 'name')
54
+ context = @resource[name]
55
+ if context.nil?
56
+ unless context_class <= Context
57
+ context_class =
58
+ Object.const_get("AdminIt::#{name.capitalize}Context")
59
+ end
60
+ context = context_class.create_class(name, @resource)
61
+ @resource.contexts << context
62
+ end
63
+ context.instance_eval(&block) if block_given?
64
+ context
65
+ end
66
+
67
+ def all(&block)
68
+ @resource.contexts.each { |c| c.instance_eval(&block) } if block_given?
69
+ end
70
+
71
+ def single(&block)
72
+ @resource.singles.each { |c| c.instance_eval(&block) } if block_given?
73
+ end
74
+
75
+ def collection(&block)
76
+ return unless block_given?
77
+ @resource.collections.each { |c| c.instance_eval(&block) }
78
+ end
79
+
80
+ def default_context(value)
81
+ return if @resource[value].nil?
82
+ @resource.instance_variable_set(:@default_context, value)
83
+ end
84
+
85
+ def icon(value = nil)
86
+ @resource.instance_variable_set(:@icon, value.to_s)
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,56 @@
1
+ # require 'simple-navigation'
2
+ # SimpleNavigation.config_file_paths << File.expand_path(File.join(%w(.. .. config)), __FILE__)
3
+
4
+ module AdminIt
5
+ class Engine < Rails::Engine
6
+ # paths['app/controllers'] = File.join('lib', 'admin_it', 'controllers')
7
+
8
+ config.to_prepare do
9
+ Rails.application.config.i18n.load_path +=
10
+ Dir[Engine.root.join('lib', 'admin_it', 'locales', '*.yml')]
11
+
12
+ unless File.basename($0) == "rake" && ARGV.include?("db:migrate")
13
+ Dir[File.join(AdminIt.config.root, '**', '*.rb')].each do |file|
14
+ require file
15
+ end
16
+ end
17
+ # Assets.register(Rails.application.assets)
18
+
19
+ # AdminIt.init
20
+
21
+ Engine.routes.draw do
22
+ AdminIt.resources.each do |name, resource|
23
+ resources(resource.plural,
24
+ controller: "admin_it/#{name}",
25
+ except: [:index]) do
26
+ resource.collections.each do |context|
27
+ next unless context.collection?
28
+ get context.context_name, on: :collection
29
+ end
30
+ unless resource.collections.empty?
31
+ get('/', on: :collection, action: resource.default_context)
32
+ end
33
+ end
34
+ end
35
+ unless AdminIt.resources.empty?
36
+ name, resource = AdminIt.resources.first
37
+ get('/',
38
+ controller: "admin_it/#{name}",
39
+ action: resource.default_context)
40
+ end
41
+ end
42
+
43
+ #AdminIt.compile_menu
44
+
45
+ ActionController::Base.module_eval do
46
+ prepend_view_path 'lib/views'
47
+ end
48
+ end
49
+ end
50
+
51
+ def self.config
52
+ config = AdminIt::Config
53
+ yield config if block_given?
54
+ config
55
+ end
56
+ end
@@ -0,0 +1,37 @@
1
+ module AdminIt
2
+ #
3
+ # Framework detection methods
4
+ #
5
+ module Env
6
+ # @private
7
+ def self.framework
8
+ return @framework unless @framework.nil?
9
+ gems = Gem.loaded_specs.keys
10
+ if gems.include?('rails')
11
+ @framework = :rails
12
+ elsif gems.include?('sinatra')
13
+ @framework = :sinatra
14
+ else
15
+ @framework = :unknown
16
+ end
17
+ end
18
+
19
+ # @private
20
+ def self.pundit?
21
+ Gem::Specification.find_by_name('pundit')
22
+ true
23
+ rescue Gem::LoadError
24
+ false
25
+ end
26
+
27
+ # @private
28
+ def self.rails?
29
+ framework == :rails
30
+ end
31
+
32
+ # @private
33
+ def self.sinatra?
34
+ framework == :sinatra
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,5 @@
1
+ module AdminIt
2
+ class Error < StandardError; end
3
+ class FieldReadError < Error; end
4
+ class FieldWriteError < Error; end
5
+ end
@@ -0,0 +1,174 @@
1
+ require File.join %w(extend_it class)
2
+ require File.join %w(extend_it dsl)
3
+ require File.join %w(extend_it callbacks)
4
+
5
+ module AdminIt
6
+ #
7
+ # Describes any field of data
8
+ #
9
+ # @author [alexiss]
10
+ #
11
+ class Field
12
+ extend ExtendIt::Class
13
+ extend DataBehavior
14
+ include ExtendIt::Callbacks
15
+
16
+ TYPES = %i(unknown integer float string date relation enum)
17
+
18
+ define_callbacks :initialize#, :configure
19
+
20
+ class << self
21
+ extend ExtendIt::Dsl
22
+
23
+ # attr_reader :field_name, :entity_class
24
+
25
+ dsl_accessor :display_name do |value = nil|
26
+ value.nil? ? default_display_name : value.to_s
27
+ end
28
+
29
+ dsl_accessor :type, default: TYPES[0] do |value|
30
+ TYPES.include?(value) ? value : TYPES[0]
31
+ end
32
+
33
+ dsl_accessor :placeholder do |value = nil|
34
+ value.nil? ? display_name : value
35
+ end
36
+
37
+ dsl_boolean :readable, :writable, :visible, :sortable
38
+
39
+ dsl_block :read, :write, :render, :display
40
+
41
+ protected
42
+
43
+ def default_display_name
44
+ name.to_s
45
+ end
46
+ end
47
+
48
+ inherited_class_reader :field_name, :entity_class
49
+
50
+ def self.create(name, _entity_class,
51
+ type: :unknown,
52
+ readable: true,
53
+ writable: true,
54
+ visible: true,
55
+ sortable: true
56
+ )
57
+ base = self
58
+ Class.new(base) do
59
+ #run_callbacks :configure do
60
+ @field_name, @entity_class = name, _entity_class
61
+ import_data_module(base)
62
+ self.readable = readable
63
+ self.writable = writable
64
+ self.visible = visible
65
+ self.sortable = sortable
66
+ self.type = type
67
+ #end
68
+ end
69
+ end
70
+
71
+ def self.hide
72
+ @visible = false
73
+ end
74
+
75
+ def self.show
76
+ @visible = true
77
+ end
78
+
79
+ class_attr_reader :entity_class, :display_name, :type
80
+ attr_writer :visible, :readable, :writable
81
+
82
+ def initialize(readable: nil, writable: nil, visible: nil, sortable: nil)
83
+ run_callbacks :initialize do
84
+ @readable = readable.nil? ? self.class.readable? : readable == true
85
+ @writable = writable.nil? ? self.class.writable? : writable == true
86
+ @visible = visible.nil? ? self.class.visible? : visible == true
87
+ @sortable = sortable.nil? ? self.class.sortable? : sortable == true
88
+ end
89
+ end
90
+
91
+ def name
92
+ @name ||= self.class.field_name
93
+ end
94
+
95
+ def readable?
96
+ @readable == true
97
+ end
98
+
99
+ def writable?
100
+ @writable == true
101
+ end
102
+
103
+ def visible?
104
+ @visible == true
105
+ end
106
+
107
+ def sortable?
108
+ @sortable == true
109
+ end
110
+
111
+ def read(entity)
112
+ unless readable?
113
+ fail FieldReadError, "Attempt to read write-only field #{name}"
114
+ end
115
+ self.class.read.nil? ? read_value(entity) : self.class.read.call(entity)
116
+ end
117
+
118
+ def show(entity)
119
+ unless readable?
120
+ fail FieldReadError, "Attempt to read write-only field #{name}"
121
+ end
122
+ self.class.display.nil? ? show_value(entity) : self.class.display.call(entity)
123
+ end
124
+
125
+ def write(entity, value)
126
+ unless writable?
127
+ fail FieldWriteError, "Attempt to write read-only field #{name}"
128
+ end
129
+ if self.class.write.nil?
130
+ write_value(entity, value)
131
+ else
132
+ self.class.write.call(entity, value)
133
+ end
134
+ entity
135
+ end
136
+
137
+ def render(entity, instance: nil)
138
+ renderer = self.class.render
139
+ return if renderer.nil?
140
+ # method used as event emmiter, call block in instance or caller
141
+ # context if it present
142
+ if instance.nil?
143
+ self.class.render.call(entity)
144
+ else
145
+ instance.instance_exec(entity, &renderer)
146
+ end
147
+ end
148
+
149
+ def input(template, entity)
150
+ typed_method = "#{type}_input".to_sym
151
+ if respond_to?(typed_method)
152
+ send typed_method, template, entity
153
+ else
154
+ Helpers::Input.new(template, self, entity: entity)
155
+ end
156
+ end
157
+
158
+ protected
159
+
160
+ def read_value(entity)
161
+ raise NotImplementedError,
162
+ "Attempt to read field #{name} with unimplemented reader"
163
+ end
164
+
165
+ def show_value(entity)
166
+ read_value(entity)
167
+ end
168
+
169
+ def write_value(entity, value)
170
+ raise NotImplementedError,
171
+ "Attempt to write to field #{name} with unimplemented writer"
172
+ end
173
+ end
174
+ end
@@ -0,0 +1 @@
1
+ require File.join %w(admin_it field field)
@@ -0,0 +1,4 @@
1
+ module AdminIt
2
+ class EntityFilter < Filter
3
+ end
4
+ end
@@ -0,0 +1,31 @@
1
+ module AdminIt
2
+ class FieldFilter < Filter
3
+ class << self
4
+ attr_reader :field
5
+
6
+ protected
7
+
8
+ def default_display_name
9
+ field.nil? ? superclass.default_display_name : field.display_name
10
+ end
11
+ end
12
+
13
+ class_attr_reader :field
14
+
15
+ def self.create(name, _resource, _field)
16
+ field_class = create_class(name, _resource)
17
+ field_class.class_eval do
18
+ _field = _field.to_sym if _field.is_a?(String)
19
+ @field =
20
+ if _field <= Field
21
+ _field
22
+ elsif _field.is_a?(Symbol)
23
+ @resource.fields.find { |fld| fld.field_name == _field }
24
+ else
25
+ nil
26
+ end
27
+ end
28
+ field_class
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,150 @@
1
+ require 'date'
2
+ require 'json'
3
+ require File.join %w(extend_it class)
4
+ require File.join %w(extend_it dsl)
5
+ require File.join %w(extend_it callbacks)
6
+ require File.join %w(extend_it asserts)
7
+ require File.join %w(extend_it symbolize)
8
+
9
+ using ExtendIt::Symbolize
10
+
11
+ module AdminIt
12
+ class Filter
13
+ extend ExtendIt::Class
14
+ extend DataBehavior
15
+ include ExtendIt::Callbacks
16
+ extend ExtendIt::Asserts
17
+
18
+ REGEXP = /\A(?<name>[a-zA-Z_][a-zA-Z0-9_]*)(?:\((?<params>[^)]*)\))?\z/
19
+
20
+ define_callbacks :save
21
+
22
+ class << self
23
+ extend ExtendIt::Dsl
24
+
25
+ attr_reader :filter_name, :resource
26
+
27
+ dsl_accessor :display_name do |value = nil|
28
+ value.nil? ? default_display_name : value.to_s
29
+ end
30
+
31
+ protected
32
+
33
+ def create_class(name, _resource)
34
+ assert_symbol(:name, binding: binding)
35
+ base = self
36
+ Class.new(base) do
37
+ @filter_name, @resource = name, _resource
38
+ @entity_class = @resource.entity_class
39
+ import_data_module(base)
40
+ end
41
+ end
42
+
43
+ def default_display_name
44
+ filter_name
45
+ end
46
+ end
47
+
48
+ def self.create(name, _resource)
49
+ create_class(name, _resource)
50
+ end
51
+
52
+ def self.load(str, filters)
53
+ m = REGEXP.match(str)
54
+ return nil if m.nil?
55
+ name = m[:name].to_sym
56
+ filter_class = filters.find { |f| f.filter_name == name }
57
+ return nil if filter_class.nil?
58
+ opts = {}
59
+ args = m[:params].nil? ? [] : m[:params].split(',').map do |param|
60
+ param.strip!
61
+ arr = param.split(':')
62
+ if arr.size > 1
63
+ opts[arr[0].strip.to_sym] = arr[1].strip
64
+ nil
65
+ else
66
+ arr[0]
67
+ end
68
+ end
69
+ args << opts unless opts.empty?
70
+ filter_class.new(*args.compact)
71
+ end
72
+
73
+ class_attr_reader :display_name
74
+
75
+ def name
76
+ @name ||= self.class.filter_name
77
+ end
78
+
79
+ def dump
80
+ args = []
81
+ opts = {}
82
+ result = ''
83
+ run_callbacks :save, arguments: { arguments: args, options: opts } do
84
+ result = "#{name}"
85
+ unless args.empty? && opts.empty?
86
+ args.concat(opts.map { |k, v| "#{k}:#{v}" })
87
+ result << "(#{args.join(',')})"
88
+ end
89
+ end
90
+ result
91
+ end
92
+
93
+ def change(str)
94
+ end
95
+
96
+ def apply(collection)
97
+ collection
98
+ end
99
+
100
+ protected
101
+
102
+ TRUE_VALUES = %i(true yes)
103
+ FALSE_VALUES = %i(false no)
104
+ NULL_VALUES = %i(nil null)
105
+ OCT_REGEXP = /\A0[0-7]+\z/
106
+ BIN_REGEXP = /\A0[bB][01]+\z/
107
+ HEX_REGEXP = /\A0[xX]\h+\z/
108
+ INT_REGEXP = /\A[+\-]?\d+\z/
109
+ FLOAT_REGEXP = /\A
110
+ [+\-]?
111
+ (?:\d*\.\d+(?:[eE][+\-]?\d+)?)|
112
+ (?:\d+(?:\.\d*)?[eE][+\-]?\d+)
113
+ \z/x
114
+ DATE = %q{[0-3]?[0-9][\/.\-][0-3]?[0-9][\/.\-](?:[0-9]{2})?[0-9]{2}}
115
+ TIME = %q{[0-2]?[0-9][.:\-][0-5]?[0-9]}
116
+ DATE_REGEXP = /\A#{DATE}\z/
117
+ TIME_REGEXP = /\A#{TIME}\z/
118
+ DATETIME_REGEXP = /\A#{DATE}(?:\s+|[\/.\-])#{TIME}\z/
119
+
120
+ def parse_argument(arg)
121
+ return arg unless arg.is_a?(String)
122
+ arg.strip!
123
+ sym = arg.downcase.to_sym
124
+ return nil if NULL_VALUES.include?(sym)
125
+ return true if TRUE_VALUES.include?(sym)
126
+ return false if FALSE_VALUES.include?(sym)
127
+ if (arg[0] == '\'' || arg[0] == '"') && arg[0] == arg[-1]
128
+ return arg[1..-2]
129
+ end
130
+ case arg
131
+ when OCT_REGEXP then arg.to_i(8)
132
+ when BIN_REGEXP then arg.to_i(2)
133
+ when HEX_REGEXP then arg.to_i(16)
134
+ when INT_REGEXP then arg.to_i(10)
135
+ when FLOAT_REGEXP then arg.to_f
136
+ when DATE_REGEXP then Date.parse(arg)
137
+ when TIME_REGEXP then Time.parse(arg)
138
+ when DATETIME_REGEXP then DateTime.parse(arg)
139
+ else JSON.parse(arg)
140
+ end
141
+ end
142
+
143
+ def create_argument(arg)
144
+ case arg
145
+ when String then "\"#{arg.gsub('"', '\\"')}\""
146
+ else arg.to_s
147
+ end
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,52 @@
1
+ module AdminIt
2
+ class ValueFilter < FieldFilter
3
+ attr_reader :values
4
+
5
+ def initialize(*values, **opts)
6
+ @values = values.map { |v| parse_argument(v) }.uniq
7
+ end
8
+
9
+ before_save do |arguments: [], options: {}|
10
+ arguments.concat(@values.map { |v| create_argument(v) })
11
+ end
12
+
13
+ def change(str)
14
+ return if str.nil? || !str.is_a?(String) || str.empty?
15
+ @values = [] unless /[+\-]/ =~ str
16
+ str.split(',').each do |param|
17
+ param.strip!
18
+ if param[0] == '-'
19
+ @values.delete_if { |v| v == parse_argument(param[1..-1]) }
20
+ else
21
+ param = param[1..-1] if param[0] == '+'
22
+ @values << parse_argument(param)
23
+ end
24
+ end
25
+ @values.uniq!
26
+ end
27
+
28
+ def all_values(collection = nil, &block)
29
+ collection ||= []
30
+ enum = Enumerator.new do |yielder|
31
+ values = collection.map { |e| self.class.field.read(e) }
32
+ values.uniq.each do |value|
33
+ yileder << {
34
+ value: value, count: values.count { |v| v == value }
35
+ }
36
+ end
37
+ end
38
+ block_given? ? enum.each(&block) : enum
39
+ end
40
+
41
+ def value(val)
42
+ create_argument(val)
43
+ end
44
+
45
+ def apply(collection)
46
+ return collection if @values.empty?
47
+ collection.select do |entity|
48
+ @values.include?(self.class.field.read(entity))
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,4 @@
1
+ require File.join %w(admin_it filters filter)
2
+ require File.join %w(admin_it filters field_filter)
3
+ require File.join %w(admin_it filters entity_filter)
4
+ require File.join %w(admin_it filters value_filter)