acts_as_table 0.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 (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