has_json_attributes_on 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2d72de8b5fe0ad71e4e6ccc7ba63a7a708916516
4
+ data.tar.gz: e4ffdff6fc8e9886df9a8e80b7b9c00bc43994a7
5
+ SHA512:
6
+ metadata.gz: f9052ae90b3e913af9aafa1689c28fe7dc1af741228e48310c6b87f6f35f73784011d0e61c36aa7dd1bd6e8d3f4788030a993c596926f1c30cffcbf7902279bf
7
+ data.tar.gz: 3e148c926b5c3f564229e5bf6f6990271ac62cda03648617789056a89376f74a34393deff32395cad9d28efe1f64e81b9ac2b014dd24921869e836e23e1e5e32
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2016 wiseallie
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,23 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'HasJsonAttributesOn'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.rdoc')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+
18
+
19
+
20
+
21
+
22
+ Bundler::GemHelper.install_tasks
23
+
@@ -0,0 +1,3 @@
1
+ module HasJsonAttributesOn
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,204 @@
1
+ require 'rails'
2
+ require 'active_record/connection_adapters/postgresql/oid/json'
3
+ require 'active_record/connection_adapters/postgresql/oid/jsonb'
4
+ require 'virtus'
5
+ require 'default_value_for'
6
+
7
+ module HasJsonAttributesOn
8
+ extend ActiveSupport::Concern
9
+
10
+ AXIOMS = {
11
+ 'Boolean' => Axiom::Types::Boolean,
12
+ 'String' => Axiom::Types::String,
13
+ 'Decimal' => Axiom::Types::Decimal,
14
+ 'Date' => Axiom::Types::Date,
15
+ 'DateTime'=> Axiom::Types::DateTime,
16
+ 'Time' => Axiom::Types::Time,
17
+ 'Float' => Axiom::Types::Float,
18
+ 'Integer' => Axiom::Types::Integer,
19
+ 'Object' => Axiom::Types::Object,
20
+ 'Array' => Axiom::Types::Array,
21
+ 'Set' => Axiom::Types::Set,
22
+ 'Hash' => Axiom::Types::Hash
23
+ }
24
+
25
+ SUPPORTED_DB_SQL_TYPES = %w(json jsonb)
26
+ VALUE_TYPE_CLASSES = {
27
+ 'json' => 'HasJsonAttributesOn::Type::Json',
28
+ 'jsonb' => 'HasJsonAttributesOn::Type::Jsonb'
29
+ }
30
+
31
+ module Type
32
+ module JsonType
33
+ extend ActiveSupport::Concern
34
+
35
+ included do
36
+ attr_accessor :virtus_model
37
+ end
38
+
39
+ class_methods do
40
+
41
+ def validate_virtus_model_attr_options!(model, attrs)
42
+ raise "Model: #{model}, attributes must be a Hash of key:attribute_name and value:attribute_type" unless attrs.is_a?(Hash)
43
+ attrs.each do |k,v|
44
+ raise "Model: #{model}, key:#{k} should be a valid hash key" if k.to_s.include?(" ");
45
+ raise "Model: #{model}, value for key:#{k} should be one of: #{AXIOMS.keys}" unless v.in?(AXIOMS.keys)
46
+ end
47
+ end
48
+
49
+ def build_virtus_model(model, data_column, attrs = {})
50
+ validate_virtus_model_attr_options!(model, attrs)
51
+ klazz_name = data_column.to_s.camelize + "DynamicType"
52
+
53
+ klazz = Class.new do
54
+ include Virtus.model
55
+
56
+ attrs.each do |attr_name, attr_type_key|
57
+ attribute attr_name.to_sym, AXIOMS[attr_type_key]
58
+ end
59
+
60
+ def self.inspect
61
+ _attrs = attribute_set.instance_variable_get("@attributes").map{|x| [x.name, x.type.inspect].join(":")}.join(", ")
62
+ "<#{name} type =>HasJsonAttributesOn::Type::JsonType attribute_set => [#{_attrs}]>"
63
+ end
64
+
65
+ def self.to_s
66
+ self.inspect
67
+ end
68
+ end
69
+
70
+ return model.send(:const_set, klazz_name, klazz)
71
+ end
72
+ end
73
+
74
+ def initialize(model, data_column, attrs = {})
75
+ @virtus_model = self.class.build_virtus_model(model, data_column, attrs)
76
+ end
77
+
78
+ def type_cast_from_user(value)
79
+ @virtus_model.new(value)
80
+ end
81
+
82
+ def type_cast_from_database(value)
83
+ @virtus_model.new(super(value))
84
+ end
85
+
86
+ def type_cast_for_database(value)
87
+ if value.is_a?(@virtus_model)
88
+ ::ActiveSupport::JSON.encode(value)
89
+ else
90
+ super
91
+ end
92
+ end
93
+ end
94
+ class Jsonb < ::ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Jsonb
95
+ include JsonType
96
+ end
97
+ class Json < ::ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Json
98
+ include JsonType
99
+ end
100
+ end
101
+
102
+ included do
103
+ extend Forwardable
104
+ end
105
+
106
+ class_methods do
107
+
108
+ def inherited(subclass)
109
+ super
110
+ if self.respond_to?(:has_json_attributes_on) && self.respond_to?(:_json_attributes_on) && self._json_attributes_on.present?
111
+ self._json_attributes_on.each do |data_column, options|
112
+ subclass.has_json_attributes_on(data_column, options[:accessors])
113
+ end
114
+ end
115
+ end
116
+
117
+ def has_json_attributes_on(data_column = :data, accessors = {})
118
+ validated_data_column, data_column_sql_type = validate_json_attributes_data_column!(data_column)
119
+ validated_accessors = validate_dynamic_accessors!(accessors)
120
+ build_json_attributes(validated_data_column, data_column_sql_type, validated_accessors)
121
+ end
122
+
123
+ private
124
+
125
+ def validate_json_attributes_data_column!(data_column)
126
+ data_column = data_column.to_sym
127
+ unless columns.map(&:name).include?(data_column.to_s)
128
+ raise ArgumentError, "Model: #{name}#has_json_attributes_on, data_column: #{data_column} does not exist as a column in database"
129
+ end
130
+
131
+ data_column_sql_type = columns.detect{|x| x.name.to_s == data_column.to_s}.sql_type
132
+ unless data_column_sql_type.in?(SUPPORTED_DB_SQL_TYPES)
133
+ raise ArgumentError, "Model: #{name}#has_json_attributes_on, data_column: #{data_column} is of sql type: #{data_column_sql_type}, supported types are:#{SUPPORTED_DB_SQL_TYPES}"
134
+ end
135
+ return [data_column, data_column_sql_type]
136
+ end
137
+
138
+
139
+ def validate_dynamic_accessors!(accessors)
140
+ raise ArgumentError, "Model: #{name}#has_json_attributes_on, accessors must be a hash" unless accessors.is_a?(Hash)
141
+ accessors = accessors.symbolize_keys
142
+ accessors.each do |k,v|
143
+ if columns.map(&:name).include?(k.to_s)
144
+ raise ArgumentError, "Model: #{name}#has_json_attributes_on, accessor key:#{k} has already been defined as the model column"
145
+ end
146
+
147
+ begin
148
+ v.assert_valid_keys(:type, :validates, :default)
149
+ rescue Exception => e
150
+ raise ArgumentError, "Model: #{name}#has_json_attributes_on, accessor key:#{k}, #{e.message}"
151
+ end
152
+ end
153
+ return accessors
154
+ end
155
+
156
+ def build_json_attributes(data_column, data_column_sql_type, accessors)
157
+ cattr_accessor :_json_attributes_on
158
+ self._json_attributes_on ||= {}.symbolize_keys
159
+ self._json_attributes_on[data_column] ||= {
160
+ accessors: {},
161
+ types: {},
162
+ default_values: {},
163
+ validations: {},
164
+ delegators: [],
165
+ data_column_sql_type: data_column_sql_type,
166
+ value_type_class: VALUE_TYPE_CLASSES[data_column_sql_type].constantize,
167
+ value_type_instance: nil
168
+ }.symbolize_keys
169
+
170
+ self._json_attributes_on[data_column][:accessors].merge!(accessors)
171
+
172
+ accessors.each do |_accessor_attribute, options|
173
+ self._json_attributes_on[data_column][:validations][_accessor_attribute] = options[:validates] if options[:validates]
174
+ self._json_attributes_on[data_column][:default_values][_accessor_attribute] = options[:default]
175
+ self._json_attributes_on[data_column][:types][_accessor_attribute] = options[:type] || 'String'
176
+ self._json_attributes_on[data_column][:delegators] += [_accessor_attribute, "#{_accessor_attribute.to_s}=".to_sym]
177
+ end
178
+
179
+ # set attribute for this class
180
+ self._json_attributes_on[data_column][:value_type_instance] = self._json_attributes_on[data_column][:value_type_class].new(self, data_column, self._json_attributes_on[data_column][:types])
181
+ attribute data_column, self._json_attributes_on[data_column][:value_type_instance]
182
+
183
+ # set the delegators
184
+ def_delegators data_column, *self._json_attributes_on[data_column][:delegators]
185
+
186
+ # set default values
187
+ self._json_attributes_on[data_column][:default_values].each do |_accessor_attribute, _default_value|
188
+ if _default_value.is_a?(Proc)
189
+ default_value_for(_accessor_attribute, &_default_value)
190
+ else
191
+ default_value_for _accessor_attribute, _default_value
192
+ end
193
+ end
194
+
195
+ # set validations
196
+ self._json_attributes_on[data_column][:validations].each do |_accessor_attribute, _attribute_validations|
197
+ validates _accessor_attribute, _attribute_validations
198
+ end
199
+
200
+ end
201
+ end
202
+ end
203
+
204
+ ActiveRecord::Base.send(:include, HasJsonAttributesOn)
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :has_json_attributes_on do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,108 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: has_json_attributes_on
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - wiseallie
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-01-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 4.2.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 4.2.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: pg
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: virtus
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 1.0.5
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 1.0.5
55
+ - !ruby/object:Gem::Dependency
56
+ name: default_value_for
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 3.0.1
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 3.0.1
69
+ description: Only supports Postgresql JSON & JSONB columns at the moment
70
+ email:
71
+ - wiseallie@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - MIT-LICENSE
77
+ - Rakefile
78
+ - lib/has_json_attributes_on.rb
79
+ - lib/has_json_attributes_on/version.rb
80
+ - lib/tasks/has_json_attributes_on_tasks.rake
81
+ homepage: https://github.com/wiseallie/has_json_attributes_on
82
+ licenses:
83
+ - MIT
84
+ metadata: {}
85
+ post_install_message:
86
+ rdoc_options: []
87
+ require_paths:
88
+ - lib
89
+ required_ruby_version: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ required_rubygems_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ requirements: []
100
+ rubyforge_project:
101
+ rubygems_version: 2.4.8
102
+ signing_key:
103
+ specification_version: 4
104
+ summary: 'ActiveRecord: Gives the ability to store attributes in one JSON or JSONB
105
+ column in the database and provides validations, typecasting and default values
106
+ on the accessors'
107
+ test_files: []
108
+ has_rdoc: