admin_it 1.0.1

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 (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)