acts_as_table 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +1 -0
  3. data/LICENSE +30 -0
  4. data/README.md +256 -0
  5. data/Rakefile +13 -0
  6. data/app/models/acts_as_table/belongs_to.rb +81 -0
  7. data/app/models/acts_as_table/column_model.rb +89 -0
  8. data/app/models/acts_as_table/foreign_key.rb +299 -0
  9. data/app/models/acts_as_table/foreign_key_map.rb +121 -0
  10. data/app/models/acts_as_table/has_many.rb +90 -0
  11. data/app/models/acts_as_table/has_many_target.rb +40 -0
  12. data/app/models/acts_as_table/lense.rb +131 -0
  13. data/app/models/acts_as_table/primary_key.rb +108 -0
  14. data/app/models/acts_as_table/record.rb +109 -0
  15. data/app/models/acts_as_table/record_error.rb +50 -0
  16. data/app/models/acts_as_table/record_model.rb +194 -0
  17. data/app/models/acts_as_table/row_model.rb +285 -0
  18. data/app/models/acts_as_table/table.rb +41 -0
  19. data/app/models/acts_as_table/value.rb +80 -0
  20. data/app/models/concerns/acts_as_table/record_model_class_methods.rb +554 -0
  21. data/app/models/concerns/acts_as_table/value_provider.rb +223 -0
  22. data/app/models/concerns/acts_as_table/value_provider_association_methods.rb +57 -0
  23. data/config/locales/en.yml +8 -0
  24. data/db/migrate/1_acts_as_table_migration.rb +186 -0
  25. data/lib/acts_as_table.rb +288 -0
  26. data/lib/acts_as_table/adapter.rb +81 -0
  27. data/lib/acts_as_table/engine.rb +14 -0
  28. data/lib/acts_as_table/headers.rb +196 -0
  29. data/lib/acts_as_table/mapper.rb +464 -0
  30. data/lib/acts_as_table/path.rb +157 -0
  31. data/lib/acts_as_table/reader.rb +149 -0
  32. data/lib/acts_as_table/version.rb +4 -0
  33. data/lib/acts_as_table/writer.rb +116 -0
  34. metadata +181 -0
@@ -0,0 +1,223 @@
1
+ module ActsAsTable
2
+ # ActsAsTable value provider (concern).
3
+ #
4
+ # @note The minimum implementation is the protected `_get_value` and `_set_value` instance methods.
5
+ module ValueProvider
6
+ extend ::ActiveSupport::Concern
7
+
8
+ class_methods do
9
+ # Returns `true` if the class is an ActsAsTable value provider. Otherwise, returns `false`.
10
+ #
11
+ # @return [Boolean]
12
+ def acts_as_table_value_provider?
13
+ false
14
+ end
15
+
16
+ # Mark the class as an ActsAsTable value provider.
17
+ #
18
+ # @return [void]
19
+ def acts_as_table_value_provider
20
+ unless self.acts_as_table_value_provider?
21
+ self.class_eval do
22
+ def self.acts_as_table_value_provider?
23
+ true
24
+ end
25
+ end
26
+
27
+ self.include(InstanceMethods)
28
+ end
29
+
30
+ return
31
+ end
32
+ end
33
+
34
+ # ActsAsTable wrapped value object.
35
+ #
36
+ # @!attribute [r] value_provider
37
+ # Returns the ActsAsTable value provider for this ActsAsTable wrapped value object.
38
+ #
39
+ # @return [ActsAsTable::ValueProvider::InstanceMethods]
40
+ # @!attribute [r] base
41
+ # Returns the record for this ActsAsTable wrapped value object.
42
+ #
43
+ # @return [ActiveRecord::Base, nil]
44
+ # @!attribute [r] source_value
45
+ # Returns the source value of this ActsAsTable wrapped value object.
46
+ #
47
+ # @return [Object, nil]
48
+ # @!attribute [r] target_value
49
+ # Returns the target value of this ActsAsTable wrapped value object.
50
+ #
51
+ # @return [Object, nil]
52
+ # @!attribute [r] options
53
+ # Returns the options for this ActsAsTable wrapped value object.
54
+ #
55
+ # @return [Hash<Symbol, Object>]
56
+ class WrappedValue
57
+ attr_reader :value_provider, :base, :source_value, :target_value, :options
58
+
59
+ # Returns a new ActsAsTable wrapped value object.
60
+ #
61
+ # @param [ActsAsTable::ValueProvider::InstanceMethods] value_provider
62
+ # @param [ActiveRecord::Base, nil] base
63
+ # @param [Object, nil] source_value
64
+ # @param [Object, nil] target_value
65
+ # @param [Hash<Symbol, Object>] options
66
+ # @option options [Boolean] :changed
67
+ # @option options [Boolean] :default
68
+ # @return [ActsAsTable::ValueProvider::WrappedValue]
69
+ def initialize(value_provider, base = nil, source_value = nil, target_value = nil, **options)
70
+ options.assert_valid_keys(:changed, :default)
71
+
72
+ @value_provider, @base, @source_value, @target_value, @options = value_provider, base, source_value, target_value, options.dup
73
+ end
74
+
75
+ # Sets the ':changed' option for this ActsAsTable wrapped value object to 'true'.
76
+ #
77
+ # @return [Boolean]
78
+ def changed!
79
+ @options[:changed] = true
80
+ end
81
+
82
+ # Returns 'true' if the value of this ActsAsTable wrapped value object has changed. Otherwise, returns 'false'.
83
+ #
84
+ # @return [Boolean]
85
+ def changed?
86
+ !!@options[:changed]
87
+ end
88
+ alias_method :changed, :changed?
89
+
90
+ # Returns 'true' if the value of this ActsAsTable wrapped value object is the default value for the value provider. Otherwise, returns 'false'.
91
+ #
92
+ # @return [Boolean]
93
+ def default?
94
+ !!@options[:default]
95
+ end
96
+ alias_method :default, :default?
97
+ end
98
+
99
+ # ActsAsTable value provider instance methods (concern).
100
+ #
101
+ # @note The design of this module is inspired by the the Haskell package, [lens](https://hackage.haskell.org/package/lens).
102
+ module InstanceMethods
103
+ extend ::ActiveSupport::Concern
104
+
105
+ # Reads the value for the given record using the given options.
106
+ #
107
+ # @param [ActiveRecord::Base, nil] base
108
+ # @param [Hash<Symbol, Object>] options
109
+ # @option options [Boolean] :default
110
+ # @return [ActsAsTable::ValueProvider::WrappedValue]
111
+ def get_value(base = nil, **options)
112
+ options.assert_valid_keys(:default)
113
+
114
+ if base.nil?
115
+ return ActsAsTable.adapter.wrap_value_for(self, base, nil, nil)
116
+ end
117
+
118
+ # @return [Object, nil]
119
+ source_value = _get_value(base)
120
+
121
+ # @return [Object, nil]
122
+ target_value = _modify_get_value_before_default(source_value)
123
+
124
+ default = \
125
+ if options[:default].try { |boolean_or_proc| _should_default?(target_value, boolean_or_proc) } && self.respond_to?(:default_value)
126
+ target_value = self.default_value
127
+
128
+ true
129
+ else
130
+ false
131
+ end
132
+
133
+ ActsAsTable.adapter.wrap_value_for(self, base, source_value, target_value, default: default)
134
+ end
135
+
136
+ # Writes the new value for the given record using the given options.
137
+ #
138
+ # @param [ActiveRecord::Base, nil] base
139
+ # @param [Object, nil] new_value
140
+ # @param [Hash<Symbol, Object>] options
141
+ # @option options [Boolean] :default
142
+ # @return [ActsAsTable::ValueProvider::WrappedValue]
143
+ def set_value(base = nil, new_value = nil, **options)
144
+ options.assert_valid_keys(:default)
145
+
146
+ if base.nil?
147
+ return ActsAsTable.adapter.wrap_value_for(self, base, nil, nil)
148
+ end
149
+
150
+ # @return [Object, nil]
151
+ target_value = _modify_set_value_before_default(new_value)
152
+
153
+ changed = false
154
+
155
+ default = \
156
+ if options[:default].try { |boolean_or_proc| _should_default?(target_value, boolean_or_proc) } && self.respond_to?(:default_value)
157
+ target_value = self.default_value
158
+
159
+ true
160
+ else
161
+ false
162
+ end
163
+
164
+ target_value, changed = *_set_value(base, target_value)
165
+
166
+ ActsAsTable.adapter.wrap_value_for(self, base, new_value, target_value, changed: changed, default: default)
167
+ end
168
+
169
+ protected
170
+
171
+ # @param [ActiveRecord::Base, nil] base
172
+ # @return [Object, nil]
173
+ def _get_value(base = nil)
174
+ nil
175
+ end
176
+
177
+ # @param [Object, nil] new_value
178
+ # @return [Object, nil]
179
+ def _modify_get_value_before_default(new_value = nil)
180
+ new_value
181
+ end
182
+
183
+ # @param [Object, nil] new_value
184
+ # @return [Object, nil]
185
+ def _modify_set_value_before_default(new_value = nil)
186
+ new_value
187
+ end
188
+
189
+ # @param [ActiveRecord::Base, nil] base
190
+ # @param [Object, nil] new_value
191
+ # @return [Array<Object>]
192
+ def _set_value(base = nil, new_value = nil)
193
+ # @return [Object, nil]
194
+ orig_value = _get_value(base)
195
+
196
+ [
197
+ orig_value,
198
+ false,
199
+ ]
200
+ end
201
+
202
+ private
203
+
204
+ # @param [Object, nil] new_value
205
+ # @param [#call, nil] default
206
+ # @return [Boolean]
207
+ def _should_default?(new_value = nil, default = nil)
208
+ if default.respond_to?(:call)
209
+ if default.respond_to?(:arity)
210
+ case default.arity
211
+ when 1 then default.call(new_value)
212
+ else new_value.instance_exec(&default)
213
+ end
214
+ else
215
+ default.call(new_value)
216
+ end
217
+ else
218
+ new_value.nil?
219
+ end
220
+ end
221
+ end
222
+ end
223
+ end
@@ -0,0 +1,57 @@
1
+ module ActsAsTable
2
+ # ActsAsTable value provider association methods (concern).
3
+ module ValueProviderAssociationMethods
4
+ extend ::ActiveSupport::Concern
5
+
6
+ class_methods do
7
+ # Returns an array of {ActiveRecord::Reflection::MacroReflection} objects for all ActsAsTable value provider associations in the class.
8
+ #
9
+ # @note If you only want to reflect on a certain association type, pass in the symbol (`:has_many`, `:has_one`, `:belongs_to`) as the first parameter.
10
+ #
11
+ # @param [Symbol, nil] macro
12
+ # @param [Hash<Symbol, Object>] options
13
+ # @option options [Array<Symbol>, nil] :only
14
+ # @option options [Array<Symbol>, nil] :except
15
+ # @return [Array<ActiveRecord::Reflection::MacroReflection>]
16
+ def reflect_on_acts_as_table_value_provider_associations(macro = nil, **options)
17
+ options.assert_valid_keys(:except, :only)
18
+
19
+ self.reflect_on_all_associations(macro).select { |reflection|
20
+ reflection.klass.acts_as_table_value_provider?
21
+ }.select { |reflection|
22
+ (options[:except].nil? || !options[:except].collect(&:to_sym).include?(reflection.name)) && (options[:only].nil? || options[:only].collect(&:to_sym).include?(reflection.name))
23
+ }
24
+ end
25
+ end
26
+
27
+ # Enumerates all associated records for all ActsAsTable value provider associations in the class.
28
+ #
29
+ # @note If you only want to reflect on a certain association type, pass in the symbol (`:has_many`, `:has_one`, `:belongs_to`) as the first parameter.
30
+ #
31
+ # @param [Symbol, nil] macro
32
+ # @param [Hash<Symbol, Object>] options
33
+ # @option options [Array<Symbol>, nil] :only
34
+ # @option options [Array<Symbol>, nil] :except
35
+ # @return [Enumerable<ActsAsTable::ValueProvider::InstanceMethods>]
36
+ def each_acts_as_table_value_provider(macro = nil, **options, &block)
37
+ ::Enumerator.new { |enumerator|
38
+ {
39
+ belongs_to: [],
40
+ has_one: [],
41
+ has_many: [:each],
42
+ has_and_belongs_to_many: [:each],
43
+ }.each do |method_name, args|
44
+ if macro.nil? || (macro == method_name)
45
+ reflections = self.class.reflect_on_acts_as_table_value_provider_associations(method_name, **options)
46
+
47
+ reflections.each do |reflection|
48
+ self.send(reflection.name).try(*args) { |value_provider|
49
+ enumerator << value_provider
50
+ }
51
+ end
52
+ end
53
+ end
54
+ }.each(&block)
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,8 @@
1
+ en:
2
+ activerecord:
3
+ errors:
4
+ models:
5
+ acts_as_table/record_model:
6
+ attributes:
7
+ base:
8
+ unreachable: "is unreachable"
@@ -0,0 +1,186 @@
1
+ if ::ActiveRecord.gem_version >= ::Gem::Version.new('5.0')
2
+ class ActsAsTableMigration < ::ActiveRecord::Migration[4.2]; end
3
+ else
4
+ class ActsAsTableMigration < ::ActiveRecord::Migration; end
5
+ end
6
+
7
+ ActsAsTableMigration.class_eval do
8
+ def self.up
9
+ create_table ActsAsTable.row_models_table do |t|
10
+ t.integer :root_record_model_id, index: true, null: false
11
+
12
+ t.string :name, null: false
13
+
14
+ t.timestamps null: false
15
+ end
16
+ add_foreign_key ActsAsTable.row_models_table, ActsAsTable.record_models_table, column: 'root_record_model_id'
17
+
18
+ create_table ActsAsTable.column_models_table do |t|
19
+ t.references :row_model, index: true, foreign_key: { to_table: ActsAsTable.row_models_table }, null: true
20
+ t.integer :position, index: true, null: false, default: 1
21
+
22
+ t.string :name, null: false
23
+ t.string :separator, null: false, default: ','
24
+
25
+ t.timestamps null: false
26
+ end
27
+
28
+ create_table ActsAsTable.record_models_table do |t|
29
+ t.references :row_model, index: true, foreign_key: { to_table: ActsAsTable.row_models_table }, null: false
30
+
31
+ t.string :class_name, null: false
32
+
33
+ t.timestamps null: false
34
+ end
35
+
36
+ create_table ActsAsTable.lenses_table do |t|
37
+ t.references :record_model, index: true, foreign_key: { to_table: ActsAsTable.record_models_table }, null: false
38
+
39
+ t.references :column_model, index: true, foreign_key: { to_table: ActsAsTable.column_models_table }
40
+
41
+ t.string :method_name, null: false
42
+ t.string :default_value
43
+
44
+ t.timestamps null: false
45
+ end
46
+
47
+ create_table ActsAsTable.foreign_keys_table do |t|
48
+ t.references :record_model, index: true, foreign_key: { to_table: ActsAsTable.record_models_table }, null: false
49
+
50
+ t.references :column_model, index: true, foreign_key: { to_table: ActsAsTable.column_models_table }
51
+
52
+ t.string :method_name, null: false
53
+ t.string :primary_key, null: false, default: 'id'
54
+ t.string :default_value
55
+ t.boolean :polymorphic, null: false, default: false
56
+
57
+ t.timestamps null: false
58
+ end
59
+
60
+ create_table ActsAsTable.foreign_key_maps_table do |t|
61
+ t.references :foreign_key, index: true, foreign_key: { to_table: ActsAsTable.foreign_keys_table }, null: false
62
+ t.integer :position, index: true, null: false, default: 1
63
+
64
+ t.string :source_value, null: false
65
+ t.string :target_value, null: false
66
+ t.boolean :regexp, null: false, default: false
67
+ t.boolean :ignore_case, null: false, default: false
68
+ t.boolean :multiline, null: false, default: false
69
+ t.boolean :extended, null: false, default: false
70
+
71
+ t.timestamps null: false
72
+ end
73
+
74
+ create_table ActsAsTable.primary_keys_table do |t|
75
+ t.references :record_model, index: true, foreign_key: { to_table: ActsAsTable.record_models_table }, null: false
76
+
77
+ t.references :column_model, index: true, foreign_key: { to_table: ActsAsTable.column_models_table }
78
+
79
+ t.string :method_name, null: false, default: 'id'
80
+
81
+ t.timestamps null: false
82
+ end
83
+
84
+ create_table ActsAsTable.belongs_tos_table do |t|
85
+ t.references :row_model, index: true, foreign_key: { to_table: ActsAsTable.row_models_table }, null: false
86
+
87
+ t.integer :source_record_model_id, index: true, null: false
88
+ t.integer :target_record_model_id, index: true, null: false
89
+
90
+ t.string :macro, null: false, default: 'belongs_to'
91
+ t.string :method_name, null: false
92
+
93
+ t.timestamps null: false
94
+ end
95
+ add_foreign_key ActsAsTable.belongs_tos_table, ActsAsTable.record_models_table, column: 'source_record_model_id'
96
+ add_foreign_key ActsAsTable.belongs_tos_table, ActsAsTable.record_models_table, column: 'target_record_model_id'
97
+
98
+ create_table ActsAsTable.has_manies_table do |t|
99
+ t.references :row_model, index: true, foreign_key: { to_table: ActsAsTable.row_models_table }, null: false
100
+
101
+ t.integer :source_record_model_id, index: true, null: false
102
+
103
+ t.string :macro, null: false, default: 'has_many'
104
+ t.string :method_name, null: false
105
+
106
+ t.timestamps null: false
107
+ end
108
+ add_foreign_key ActsAsTable.has_manies_table, ActsAsTable.record_models_table, column: 'source_record_model_id'
109
+
110
+ create_table ActsAsTable.has_many_targets_table do |t|
111
+ t.references :has_many, index: true, foreign_key: { to_table: ActsAsTable.has_manies_table }, null: false
112
+ t.integer :position, index: true, null: false, default: 1
113
+
114
+ t.references :record_model, index: true, foreign_key: { to_table: ActsAsTable.record_models_table }, null: false
115
+
116
+ t.timestamps null: false
117
+ end
118
+
119
+ create_table ActsAsTable.tables_table do |t|
120
+ t.references :row_model, index: true, foreign_key: { to_table: ActsAsTable.row_models_table }, null: false
121
+
122
+ t.timestamps null: false
123
+
124
+ t.integer :records_count, null: false, default: 0
125
+ end
126
+
127
+ create_table ActsAsTable.records_table do |t|
128
+ t.references :table, index: true, foreign_key: { to_table: ActsAsTable.tables_table }, null: false
129
+ t.references :record_model, index: true, foreign_key: { to_table: ActsAsTable.record_models_table }, null: false
130
+ t.integer :position, index: true, null: false, default: 1
131
+
132
+ t.references :base, polymorphic: true, index: true
133
+
134
+ t.boolean :record_model_changed, null: false, default: false
135
+
136
+ t.timestamps null: false
137
+
138
+ t.integer :record_errors_count, null: false, default: 0
139
+ t.integer :values_count, null: false, default: 0
140
+ end
141
+
142
+ create_table ActsAsTable.record_errors_table do |t|
143
+ t.references :record, index: true, foreign_key: { to_table: ActsAsTable.records_table }, null: false
144
+
145
+ t.references :value, index: true, foreign_key: { to_table: ActsAsTable.values_table }
146
+
147
+ t.string :attribute_name, null: false
148
+ t.string :message, null: false
149
+
150
+ t.timestamps null: false
151
+ end
152
+
153
+ create_table ActsAsTable.values_table do |t|
154
+ t.references :record, index: true, foreign_key: { to_table: ActsAsTable.records_table }, null: false
155
+ t.references :value_provider, polymorphic: true, index: true, null: false
156
+
157
+ t.references :column_model, index: true, foreign_key: { to_table: ActsAsTable.column_models_table }
158
+ t.integer :position, index: true
159
+
160
+ t.text :source_value
161
+ t.text :target_value
162
+ t.boolean :value_provider_changed, null: false, default: false
163
+
164
+ t.timestamps null: false
165
+
166
+ t.integer :record_errors_count, null: false, default: 0
167
+ end
168
+ end
169
+
170
+ def self.down
171
+ drop_table ActsAsTable.belongs_tos_table
172
+ drop_table ActsAsTable.column_models_table
173
+ drop_table ActsAsTable.foreign_key_maps_table
174
+ drop_table ActsAsTable.foreign_keys_table
175
+ drop_table ActsAsTable.has_manies_table
176
+ drop_table ActsAsTable.has_many_targets_table
177
+ drop_table ActsAsTable.lenses_table
178
+ drop_table ActsAsTable.primary_keys_table
179
+ drop_table ActsAsTable.record_errors_table
180
+ drop_table ActsAsTable.record_models_table
181
+ drop_table ActsAsTable.records_table
182
+ drop_table ActsAsTable.row_models_table
183
+ drop_table ActsAsTable.tables_table
184
+ drop_table ActsAsTable.values_table
185
+ end
186
+ end