store_schema 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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d13d481527cfe8e42df69d8c0ac8277a09d0b2f7
4
+ data.tar.gz: 4bfba69ec1c4ce763a544aa8eb1cdd3ec7aad8eb
5
+ SHA512:
6
+ metadata.gz: 870cf0a943b7c6c27c8a11eb961b65ae05033471f76d755391baf1a7e8f88be8bd959c3b7d3a4faa0c3c323ef91119909414e5ef5099d08b8b463d6f5968a0d8
7
+ data.tar.gz: b8db099f6f49b350e53aa39483d7200db482b40f3deacab9b1151fff93ed9d5132de10d22e3f0e81122ae90ebe3f4202103851674ad9c9c12954637a0466d859
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+ gem "activerecord", "~> 4.0.0"
3
+ gemspec path: "../"
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+ gem "activerecord", "~> 4.1.0"
3
+ gemspec path: "../"
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+ gem "activerecord", "4.2.0.beta1"
3
+ gemspec path: "../"
data/.gitignore ADDED
@@ -0,0 +1,23 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
23
+ .DS_Store
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/.travis.yml ADDED
@@ -0,0 +1,9 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ - 2.1.0
5
+ - rbx-2.2
6
+ gemfile:
7
+ - .gemfiles/4.0.gemfile
8
+ - .gemfiles/4.1.gemfile
9
+ - .gemfiles/4.2.gemfile
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source "https://rubygems.org"
2
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Michael van Rooijen
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,157 @@
1
+ # StoreSchema
2
+
3
+ [![Code Climate](https://codeclimate.com/github/meskyanichi/store_schema.png)](https://codeclimate.com/github/meskyanichi/store_schema)
4
+ [![Build Status](https://travis-ci.org/meskyanichi/store_schema.svg)](https://travis-ci.org/meskyanichi/store_schema)
5
+
6
+ StoreSchema, for Rails/ActiveRecord 4.0.0+, enhances `ActiveRecord::Base.store_accessor` with data conversion capabilities.
7
+
8
+ This library was developed for- and extracted from [HireFire].
9
+
10
+
11
+ ### Compatibility
12
+
13
+ - Rails/ActiveRecord 4.0.0+
14
+ - Ruby (MRI) 2.0+
15
+ - Ruby (RBX) 2.2+
16
+
17
+
18
+ ### Installation
19
+
20
+ Distributed at a gem, hosted at [RubyGems.org].
21
+
22
+ ```rb
23
+ gem "store_schema"
24
+ ```
25
+
26
+
27
+ ### Example
28
+
29
+ This example assumes you have a `websites` table with a column named
30
+ `config` of type `text`.
31
+
32
+ Define a model and use `store_schema`.
33
+
34
+ ```rb
35
+ class Website < ActiveRecord::Base
36
+
37
+ # Tell ActiveRecord that we want to serialize the :config attribute
38
+ # and store the serialized data as text in the config column.
39
+ #
40
+ # If you're using PostreSQL's "hstore" column-type instead of the
41
+ # "text" column-type, you don't have to define `serialize :config`.
42
+ #
43
+ serialize :config
44
+
45
+ # Define a schema for the store. This syntax is similar to
46
+ # ActiveRecord::Migration.
47
+ #
48
+ store_schema :config do |s|
49
+ s.string :name
50
+ s.integer :visitors
51
+ s.float :apdex
52
+ s.boolean :ssl
53
+ s.datetime :published_at
54
+ end
55
+ end
56
+ ```
57
+
58
+ Now you can get and set attributes on the `websites.config` column using
59
+ the generated accessors.
60
+
61
+ ```rb
62
+ website = Website.create(
63
+ name: "Example Website",
64
+ visitors: 1337,
65
+ apdex: 1.0,
66
+ ssl: true,
67
+ published_at: Time.now
68
+ )
69
+
70
+ p website.name # => "Example Website" (String)
71
+ p website.visitors # => 1337 (Fixnum)
72
+ p website.apdex # => 1.0 (Float)
73
+ p website.ssl # => true (TrueClass)
74
+ p website.published_at # => "Thu, 18 Sep 2014 23:18:11 +0000" (DateTime)
75
+
76
+ p website.config
77
+ # =>
78
+ # {
79
+ # "name" => "Example Website",
80
+ # "visitors" => "1337",
81
+ # "apdex" => "1.0",
82
+ # "ssl" => "t",
83
+ # "published_at" => "2014-09-18 23:18:11.583168000"
84
+ # }
85
+ ```
86
+
87
+ That's it. This is similar to just using `store_accessor`, except that
88
+ `store_schema` is more strict as to what data types are stored. It attempts
89
+ to stay consistent with ActiveRecord's column conventions such as storing
90
+ booleans (`0`, `"0"`, `1`, `"1"`, `"t"`, `"T"`, `"f"`, `"F"`, `true`,
91
+ `"true"`, `"TRUE"`, `false`, `"false"`, `"FALSE"`, `"on"`, `"ON"`, `"off"`,
92
+ `"OFF"`) as `"t"` and `"f"`, storing `Time`, `Date` as `DateTime`,
93
+ ensuring `Time` is UTC prior to being stored, and more.
94
+
95
+ When accessing stored data, it properly converts them to their data types.
96
+ For example, `"t"` is converted to a TrueClass, and
97
+ `"2014-09-18 23:18:11.583168000"` is converted back to a DateTime.
98
+ See above example.
99
+
100
+ If you need to be able to query these serialized attributes,
101
+ consider using [PostgreSQL's HStore Extension]. If you do not need to
102
+ be able to query the serialized data, you can simply use a text-type column
103
+ and use the `serialize` method in your model which works with any SQL database.
104
+
105
+
106
+ ### Contributing
107
+
108
+ Contributions are welcome, but please conform to these requirements:
109
+
110
+ - Ruby (MRI) 2.0+
111
+ - Ruby (RBX) 2.2+
112
+ - ActiveRecord 4.0.0+
113
+ - 100% Spec Coverage
114
+ - Generated by when running the test suite
115
+ - 100% [Passing Specs]
116
+ - Run test suite with `$ rspec spec`
117
+ - 4.0 [Code Climate Score]
118
+ - Run `$ rubycritic lib` to generate the score locally and receive tips
119
+ - No code smells
120
+ - No duplication
121
+
122
+ To start contributing, fork the project, clone it, and install the development dependencies:
123
+
124
+ ```
125
+ git clone git@github.com:USERNAME/store_schema.git
126
+ cd store_schema
127
+ bundle
128
+ ```
129
+
130
+ Ensure that everything works:
131
+
132
+ ```
133
+ rspec spec
134
+ rubycritic lib
135
+ ```
136
+
137
+ Create a new branch and start hacking:
138
+
139
+ ```
140
+ git checkout -b my-contributions
141
+ ```
142
+
143
+ Submit a pull request.
144
+
145
+
146
+ ### Author / License
147
+
148
+ Copyright (c) 2014 Michael van Rooijen ( [@meskyanichi] )<br />
149
+ Released under the MIT [License].
150
+
151
+ [@meskyanichi]: https://twitter.com/meskyanichi
152
+ [HireFire]: http://hirefire.io
153
+ [Passing Specs]: https://travis-ci.org/meskyanichi/store_schema
154
+ [Code Climate Score]: https://codeclimate.com/github/meskyanichi/store_schema
155
+ [License]: https://github.com/meskyanichi/store_schema/blob/master/LICENSE
156
+ [RubyGems.org]: https://rubygems.org/gems/store_schema
157
+ [PostgreSQL's HStore Extension]: http://www.postgresql.org/docs/9.3/static/hstore.html
data/Rakefile ADDED
@@ -0,0 +1,4 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+ RSpec::Core::RakeTask.new(:spec)
4
+ task default: :spec
@@ -0,0 +1,89 @@
1
+ class StoreSchema::AccessorDefiner
2
+ class InvalidValueType < StandardError; end
3
+
4
+ # @return [Class]
5
+ #
6
+ attr_reader :klass
7
+
8
+ # @return [Symbol]
9
+ #
10
+ attr_reader :column
11
+
12
+ # @return [Symbol]
13
+ #
14
+ attr_reader :type
15
+
16
+ # @return [Symbol]
17
+ #
18
+ attr_reader :attribute
19
+
20
+ # @param klass [Class] the class to define the accessor on
21
+ # @param column [Symbol] the name of the column to define the accessor on
22
+ # @param type [Symbol] the data type of the {#attribute}
23
+ # @param attribute [Symbol] the name of the {#column}'s attribute
24
+ #
25
+ def initialize(klass, column, type, attribute)
26
+ @klass = klass
27
+ @column = column
28
+ @type = type
29
+ @attribute = attribute
30
+ end
31
+
32
+ # Defines all necessary accessors on {#klass}.
33
+ #
34
+ def define
35
+ define_store_accessor
36
+ define_getter
37
+ define_setter
38
+ end
39
+
40
+ private
41
+
42
+ # Defines the standard store accessor.
43
+ #
44
+ def define_store_accessor
45
+ klass.store_accessor(column, attribute)
46
+ end
47
+
48
+ # Enhances the store getter by adding data conversion capabilities.
49
+ #
50
+ def define_getter
51
+ _type = type
52
+
53
+ klass.send(:define_method, attribute) do
54
+ value = super()
55
+
56
+ if value.is_a?(NilClass)
57
+ value
58
+ else
59
+ StoreSchema::Converter.new(value, _type).from_db
60
+ end
61
+ end
62
+ end
63
+
64
+ # Enhances the store setter by adding data conversion capabilities.
65
+ #
66
+ def define_setter
67
+ _klass = klass
68
+ _column = column
69
+ _type = type
70
+ _attribute = attribute
71
+
72
+ klass.send(:define_method, "#{attribute}=") do |value|
73
+ if value.is_a?(NilClass)
74
+ super(value)
75
+ else
76
+ converted_value = StoreSchema::Converter
77
+ .new(value, _type).to_db
78
+
79
+ if converted_value
80
+ super(converted_value)
81
+ else
82
+ raise InvalidValueType,
83
+ "#{value} (#{value.class}) for " +
84
+ "#{_klass}##{_column}.#{_attribute} (#{_type})"
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,66 @@
1
+ class StoreSchema::Configuration
2
+
3
+ # @return [Symbol]
4
+ #
5
+ attr_reader :column
6
+
7
+ # @return [Hash]
8
+ #
9
+ attr_reader :attributes
10
+
11
+ # @param column [Symbol] the table column to generate the accessors for
12
+ #
13
+ def initialize(column)
14
+ @column = column
15
+ @attributes = {}
16
+ end
17
+
18
+ # @param attribute [Symbol] the name of the attribute on {#column}
19
+ # for which to generate a String-type accessor
20
+ #
21
+ def string(attribute)
22
+ attributes[attribute] = :string
23
+ end
24
+
25
+ # @param attribute [Symbol] the name of the attribute on {#column}
26
+ # for which to generate an Integer-type accessor
27
+ #
28
+ def integer(attribute)
29
+ attributes[attribute] = :integer
30
+ end
31
+
32
+ # @param attribute [Symbol] the name of the attribute on {#column}
33
+ # for which to generate a Float-type accessor
34
+ #
35
+ def float(attribute)
36
+ attributes[attribute] = :float
37
+ end
38
+
39
+ # @param attribute [Symbol] the name of the attribute on {#column}
40
+ # for which to generate a DateTime-type accessor
41
+ #
42
+ def datetime(attribute)
43
+ attributes[attribute] = :datetime
44
+ end
45
+
46
+ # @param attribute [Symbol] the name of the attribute on {#column}
47
+ # for which to generate a Boolean-type accessor
48
+ #
49
+ def boolean(attribute)
50
+ attributes[attribute] = :boolean
51
+ end
52
+
53
+ private
54
+
55
+ # Iterates over all defined {#attributes} and defines the
56
+ # necessary accessors for them.
57
+ #
58
+ # @param klass [Class] the class to define the accessors on
59
+ #
60
+ def configure(klass)
61
+ attributes.each do |attribute, type|
62
+ StoreSchema::AccessorDefiner
63
+ .new(klass, column, type, attribute).define
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,12 @@
1
+ module StoreSchema::Converter::Base
2
+
3
+ # @return [Object]
4
+ #
5
+ attr_reader :value
6
+
7
+ # @param [Object] value
8
+ #
9
+ def initialize(value)
10
+ @value = value
11
+ end
12
+ end
@@ -0,0 +1,49 @@
1
+ require_relative "base"
2
+
3
+ class StoreSchema::Converter::Boolean
4
+ include StoreSchema::Converter::Base
5
+
6
+ # @return [String] the database representation of a true value.
7
+ #
8
+ DB_TRUE_VALUE = "t"
9
+
10
+ # @return [Array] all the values that are considered to be truthy.
11
+ #
12
+ TRUE_VALUES = [true, 1, "1", "t", "T", "true", "TRUE", "on", "ON"]
13
+
14
+ # @return [Array] all the values that are considered to be falsy.
15
+ #
16
+ FALSE_VALUES = [false, 0, "0", "f", "F", "false", "FALSE", "off", "OFF"]
17
+
18
+ # @return [Object]
19
+ #
20
+ attr_reader :value
21
+
22
+ # @param [Object] value
23
+ #
24
+ def initialize(value)
25
+ @value = value
26
+ end
27
+
28
+ # Converts the {#value} to a database-storable value.
29
+ #
30
+ # @return [String, false] false if {#value} is an invalid date-type.
31
+ #
32
+ def to_db
33
+ if TRUE_VALUES.include?(value)
34
+ "t"
35
+ elsif FALSE_VALUES.include?(value)
36
+ "f"
37
+ else
38
+ false
39
+ end
40
+ end
41
+
42
+ # Converts the {#value} to a Ruby-type value.
43
+ #
44
+ # @return [true, false]
45
+ #
46
+ def from_db
47
+ value == DB_TRUE_VALUE
48
+ end
49
+ end
@@ -0,0 +1,38 @@
1
+ require_relative "base"
2
+
3
+ class StoreSchema::Converter::DateTime
4
+ include StoreSchema::Converter::Base
5
+
6
+ # @return [String] the database format for storing a DateTime object.
7
+ #
8
+ DATETIME_DB_FORMAT = "%Y-%m-%d %H:%M:%S.%N"
9
+
10
+ # Converts the {#value} to a database-storable value.
11
+ #
12
+ # @return [String, false] false if {#value} is an invalid date-type.
13
+ #
14
+ def to_db
15
+ begin
16
+ case value
17
+ when ::DateTime, ::Date
18
+ value.strftime(DATETIME_DB_FORMAT)
19
+ when ::Time
20
+ value.utc.strftime(DATETIME_DB_FORMAT)
21
+ when ::String
22
+ ::DateTime.parse(value).strftime(DATETIME_DB_FORMAT)
23
+ else
24
+ false
25
+ end
26
+ rescue
27
+ false
28
+ end
29
+ end
30
+
31
+ # Converts the {#value} to a Ruby-type value.
32
+ #
33
+ # @return [DateTime]
34
+ #
35
+ def from_db
36
+ ::DateTime.parse(value)
37
+ end
38
+ end
@@ -0,0 +1,34 @@
1
+ require_relative "base"
2
+
3
+ class StoreSchema::Converter::Float
4
+ include StoreSchema::Converter::Base
5
+
6
+ # Converts the {#value} to a database-storable value.
7
+ #
8
+ # @return [String, false] false if {#value} is an invalid date-type.
9
+ #
10
+ def to_db
11
+ case value
12
+ when ::Integer
13
+ value.to_f.to_s
14
+ when ::Float
15
+ value.to_s
16
+ when ::String
17
+ if value =~ /^\d+|\d+\.\d+$/
18
+ value.to_f.to_s
19
+ else
20
+ false
21
+ end
22
+ else
23
+ false
24
+ end
25
+ end
26
+
27
+ # Converts the {#value} to a Ruby-type value.
28
+ #
29
+ # @return [Float]
30
+ #
31
+ def from_db
32
+ value.to_f
33
+ end
34
+ end
@@ -0,0 +1,32 @@
1
+ require_relative "base"
2
+
3
+ class StoreSchema::Converter::Integer
4
+ include StoreSchema::Converter::Base
5
+
6
+ # Converts the {#value} to a database-storable value.
7
+ #
8
+ # @return [String, false] false if {#value} is an invalid date-type.
9
+ #
10
+ def to_db
11
+ case value
12
+ when ::Integer
13
+ value.to_s
14
+ when ::String
15
+ if value =~ /^\d+$/
16
+ value
17
+ else
18
+ false
19
+ end
20
+ else
21
+ false
22
+ end
23
+ end
24
+
25
+ # Converts the {#value} to a Ruby-type value.
26
+ #
27
+ # @return [Integer]
28
+ #
29
+ def from_db
30
+ value.to_i
31
+ end
32
+ end
@@ -0,0 +1,25 @@
1
+ require_relative "base"
2
+
3
+ class StoreSchema::Converter::String
4
+ include StoreSchema::Converter::Base
5
+
6
+ # Simply returns {#value} if it's a String.
7
+ #
8
+ # @return [String, false] false if {#value} is an invalid date-type.
9
+ #
10
+ def to_db
11
+ if value.is_a?(::String)
12
+ value
13
+ else
14
+ false
15
+ end
16
+ end
17
+
18
+ # Simply returns {#value} since it's already a String-type.
19
+ #
20
+ # @return [String]
21
+ #
22
+ def from_db
23
+ value
24
+ end
25
+ end
@@ -0,0 +1,50 @@
1
+ class StoreSchema::Converter
2
+ require_relative "converter/string"
3
+ require_relative "converter/integer"
4
+ require_relative "converter/float"
5
+ require_relative "converter/date_time"
6
+ require_relative "converter/boolean"
7
+
8
+ # @return [Hash] a mapping between the configuration block
9
+ # and the converter classes.
10
+ #
11
+ MAPPING = {
12
+ string: String,
13
+ integer: Integer,
14
+ float: Float,
15
+ datetime: DateTime,
16
+ boolean: Boolean
17
+ }
18
+
19
+ # @return [Object]
20
+ #
21
+ attr_reader :value
22
+
23
+ # @return [Symbol]
24
+ #
25
+ attr_reader :type
26
+
27
+ # @param value [Object] any kind of value that should be stored
28
+ # @param type [Symbol] the type of the value
29
+ #
30
+ def initialize(value, type)
31
+ @value = value
32
+ @type = type
33
+ end
34
+
35
+ # Converts {#value} from a Ruby-type value to a database-storable value.
36
+ #
37
+ # @return [String]
38
+ #
39
+ def to_db
40
+ MAPPING[type].new(value).to_db
41
+ end
42
+
43
+ # Converts {#value} from a database-storable value to a Ruby-type value.
44
+ #
45
+ # @return [Object]
46
+ #
47
+ def from_db
48
+ MAPPING[type].new(value).from_db
49
+ end
50
+ end
@@ -0,0 +1,36 @@
1
+ module StoreSchema::Module
2
+
3
+ def self.included(base)
4
+ base.extend(ClassMethods)
5
+ end
6
+
7
+ module ClassMethods
8
+
9
+ # @example
10
+ #
11
+ # # Gemfile
12
+ # gem "store_schema"
13
+ #
14
+ # # app/models/project.rb
15
+ # class Website < ActiveRecord::Base
16
+ #
17
+ # store_schema :config do |s|
18
+ # s.string :name
19
+ # s.integer :visitors
20
+ # s.float :apdex
21
+ # s.boolean :ssl
22
+ # s.datetime :published_at
23
+ # end
24
+ # end
25
+ #
26
+ # @param column [Symbol] name of the table column
27
+ # @param block [Proc] the configuration block
28
+ #
29
+ def store_schema(column, &block)
30
+ StoreSchema::Configuration.new(column).tap do |config|
31
+ yield(config)
32
+ config.send(:configure, self)
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,3 @@
1
+ module StoreSchema
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,11 @@
1
+ module StoreSchema
2
+ require "store_schema/accessor_definer"
3
+ require "store_schema/configuration"
4
+ require "store_schema/converter"
5
+ require "store_schema/module"
6
+ require "store_schema/version"
7
+ end
8
+
9
+ if defined?(ActiveRecord::Base)
10
+ ActiveRecord::Base.send(:include, StoreSchema::Module)
11
+ end
@@ -0,0 +1,172 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe "StoreSchema" do
4
+
5
+ let(:website) { Website.new }
6
+
7
+ describe "nil" do
8
+
9
+ it "should stay nil" do
10
+ website.update_attribute(:name, nil)
11
+ expect(website.reload.name).to be_nil
12
+ end
13
+ end
14
+
15
+ describe "string" do
16
+
17
+ it "should remain a string" do
18
+ website.update_attribute(:name, "Store Schema")
19
+ expect(website.reload.name).to eq("Store Schema")
20
+ end
21
+
22
+ it "does not accept invalid data types" do
23
+ expect { website.update_attribute(:name, true) }
24
+ .to raise_error(
25
+ StoreSchema::AccessorDefiner::InvalidValueType,
26
+ "true (TrueClass) for Website#config.name (string)"
27
+ )
28
+ end
29
+ end
30
+
31
+ describe "integer" do
32
+
33
+ it "should cast to string and back to integer" do
34
+ website.update_attribute(:visitors, 1337)
35
+ expect(website.config["visitors"]).to eq("1337")
36
+ expect(website.visitors).to eq(1337)
37
+ end
38
+
39
+ it "should accept a string value of a digit" do
40
+ website.update_attribute(:visitors, "1337")
41
+ expect(website.config["visitors"]).to eq("1337")
42
+ expect(website.visitors).to eq(1337)
43
+ end
44
+
45
+ it "does not accept invalid string formats" do
46
+ expect { website.update_attribute(:visitors, "abc") }
47
+ .to raise_error(
48
+ StoreSchema::AccessorDefiner::InvalidValueType,
49
+ "abc (String) for Website#config.visitors (integer)"
50
+ )
51
+ end
52
+
53
+ it "does not accept invalid data types" do
54
+ expect { website.update_attribute(:visitors, true) }
55
+ .to raise_error(
56
+ StoreSchema::AccessorDefiner::InvalidValueType,
57
+ "true (TrueClass) for Website#config.visitors (integer)"
58
+ )
59
+ end
60
+ end
61
+
62
+ describe "float" do
63
+
64
+ it "should cast to string and back to float" do
65
+ website.update_attribute(:apdex, 0.9)
66
+ expect(website.config["apdex"]).to eq("0.9")
67
+ expect(website.apdex).to eq(0.9)
68
+ end
69
+
70
+ it "should accept a string value of a float" do
71
+ website.update_attribute(:apdex, "0.9")
72
+ expect(website.config["apdex"]).to eq("0.9")
73
+ expect(website.apdex).to eq(0.9)
74
+ end
75
+
76
+ it "should accept an integer value" do
77
+ website.update_attribute(:apdex, 1)
78
+ expect(website.config["apdex"]).to eq("1.0")
79
+ expect(website.apdex).to eq(1.0)
80
+ end
81
+
82
+ it "should accept a string value of an integer" do
83
+ website.update_attribute(:apdex, "1")
84
+ expect(website.config["apdex"]).to eq("1.0")
85
+ expect(website.apdex).to eq(1.0)
86
+ end
87
+
88
+ it "does not accept invalid string formats" do
89
+ expect { website.update_attribute(:apdex, "abc") }
90
+ .to raise_error(
91
+ StoreSchema::AccessorDefiner::InvalidValueType,
92
+ "abc (String) for Website#config.apdex (float)"
93
+ )
94
+ end
95
+
96
+ it "does not accept invalid data types" do
97
+ expect { website.update_attribute(:apdex, true) }
98
+ .to raise_error(
99
+ StoreSchema::AccessorDefiner::InvalidValueType,
100
+ "true (TrueClass) for Website#config.apdex (float)"
101
+ )
102
+ end
103
+ end
104
+
105
+ describe "boolean" do
106
+
107
+ it "should cast to string and back to boolean" do
108
+ StoreSchema::Converter::Boolean::TRUE_VALUES.each do |value|
109
+ website.update_attribute(:ssl, value)
110
+ expect(website.config["ssl"]).to eq("t")
111
+ expect(website.ssl).to eq(true)
112
+ end
113
+
114
+ StoreSchema::Converter::Boolean::FALSE_VALUES.each do |value|
115
+ website.update_attribute(:ssl, value)
116
+ expect(website.config["ssl"]).to eq("f")
117
+ expect(website.ssl).to eq(false)
118
+ end
119
+ end
120
+
121
+ it "does not accept invalid data types" do
122
+ expect { website.update_attribute(:ssl, "abc") }
123
+ .to raise_error(
124
+ StoreSchema::AccessorDefiner::InvalidValueType,
125
+ "abc (String) for Website#config.ssl (boolean)"
126
+ )
127
+ end
128
+ end
129
+
130
+ describe "datetime" do
131
+
132
+ it "should cast Date to string and back to datetime" do
133
+ value = Date.new(2020)
134
+ website.update_attribute(:published_at, value)
135
+ expect(website.config["published_at"]).to eq("2020-01-01 00:00:00.000000000")
136
+ expect(website.published_at).to eq("Wed, 01 Jan 2020 00:00:00.000000000 +0000")
137
+ expect(website.published_at.class).to eq(DateTime)
138
+ end
139
+
140
+ it "should cast Time to string and back to datetime" do
141
+ value = Time.utc(2020)
142
+ website.update_attribute(:published_at, value)
143
+ expect(website.config["published_at"]).to eq("2020-01-01 00:00:00.000000000")
144
+ expect(website.published_at).to eq("Wed, 01 Jan 2020 00:00:00.000000000 +0000")
145
+ expect(website.published_at.class).to eq(DateTime)
146
+ end
147
+
148
+ it "should cast DateTime to string and back to datetime" do
149
+ value = DateTime.new(2020)
150
+ website.update_attribute(:published_at, value)
151
+ expect(website.config["published_at"]).to eq("2020-01-01 00:00:00.000000000")
152
+ expect(website.published_at).to eq("Wed, 01 Jan 2020 00:00:00.000000000 +0000")
153
+ expect(website.published_at.class).to eq(DateTime)
154
+ end
155
+
156
+ it "does not accept invalid string formats" do
157
+ expect { website.update_attribute(:published_at, "abc") }
158
+ .to raise_error(
159
+ StoreSchema::AccessorDefiner::InvalidValueType,
160
+ "abc (String) for Website#config.published_at (datetime)"
161
+ )
162
+ end
163
+
164
+ it "does not accept invalid data types" do
165
+ expect { website.update_attribute(:published_at, true) }
166
+ .to raise_error(
167
+ StoreSchema::AccessorDefiner::InvalidValueType,
168
+ "true (TrueClass) for Website#config.published_at (datetime)"
169
+ )
170
+ end
171
+ end
172
+ end
@@ -0,0 +1,21 @@
1
+ $LOAD_PATH.unshift(File.expand_path("../../lib", __FILE__))
2
+ require File.expand_path("../support/env", __FILE__)
3
+
4
+ require "database_cleaner"
5
+ require "simplecov"
6
+ SimpleCov.start
7
+
8
+ require "store_schema"
9
+ require File.expand_path("../support/models", __FILE__)
10
+
11
+ RSpec.configure do |config|
12
+ config.mock_framework = :mocha
13
+
14
+ config.before do
15
+ DatabaseCleaner.start
16
+ end
17
+
18
+ config.after do
19
+ DatabaseCleaner.clean
20
+ end
21
+ end
@@ -0,0 +1,17 @@
1
+ ActiveRecord::Base.logger = Logger.new(LOG_PATH)
2
+ ActiveRecord::Base.establish_connection(
3
+ adapter: "sqlite3", database: ":memory:"
4
+ )
5
+
6
+ class Schema < ActiveRecord::Migration
7
+
8
+ def change
9
+ create_table :websites do |t|
10
+ t.text :config
11
+ end
12
+ end
13
+ end
14
+
15
+ silence_stream(STDOUT) do
16
+ Schema.new.change
17
+ end
@@ -0,0 +1,14 @@
1
+ require "fileutils"
2
+ require "logger"
3
+ require "pry"
4
+ require "sqlite3"
5
+ require "active_record"
6
+
7
+ ROOT_PATH = File.expand_path("../../..", __FILE__)
8
+ SPEC_PATH = File.join(ROOT_PATH, "spec")
9
+ TMP_PATH = File.join(ROOT_PATH, "tmp")
10
+ LOG_PATH = File.join(TMP_PATH, "store_schema.log")
11
+
12
+ FileUtils.mkdir_p(TMP_PATH)
13
+
14
+ require "#{SPEC_PATH}/support/db"
@@ -0,0 +1,12 @@
1
+ class Website < ActiveRecord::Base
2
+
3
+ serialize :config
4
+
5
+ store_schema :config do |s|
6
+ s.string :name
7
+ s.integer :visitors
8
+ s.float :apdex
9
+ s.boolean :ssl
10
+ s.datetime :published_at
11
+ end
12
+ end
@@ -0,0 +1,29 @@
1
+ lib = File.expand_path("../lib", __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require "store_schema/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "store_schema"
7
+ spec.version = StoreSchema::VERSION
8
+ spec.authors = ["Michael van Rooijen"]
9
+ spec.email = ["michael@vanrooijen.io"]
10
+ spec.summary = %q{Enhances ActiveRecord::Base.store_accessor with data conversion capabilities.}
11
+ spec.homepage = "http://meskyanichi.github.io/store_schema/"
12
+ spec.license = "MIT"
13
+
14
+ spec.files = `git ls-files -z`.split("\x0")
15
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
16
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
17
+ spec.require_paths = ["lib"]
18
+
19
+ spec.add_dependency "activerecord", ">= 4.0.0"
20
+ spec.add_development_dependency "bundler"
21
+ spec.add_development_dependency "rake"
22
+ spec.add_development_dependency "rspec"
23
+ spec.add_development_dependency "mocha"
24
+ spec.add_development_dependency "sqlite3"
25
+ spec.add_development_dependency "database_cleaner"
26
+ spec.add_development_dependency "pry"
27
+ spec.add_development_dependency "simplecov"
28
+ spec.add_development_dependency "yard"
29
+ end
metadata ADDED
@@ -0,0 +1,218 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: store_schema
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Michael van Rooijen
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-09-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activerecord
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 4.0.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.0.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
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: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: mocha
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: sqlite3
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: database_cleaner
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: pry
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: simplecov
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: yard
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ description:
154
+ email:
155
+ - michael@vanrooijen.io
156
+ executables: []
157
+ extensions: []
158
+ extra_rdoc_files: []
159
+ files:
160
+ - ".gemfiles/4.0.gemfile"
161
+ - ".gemfiles/4.1.gemfile"
162
+ - ".gemfiles/4.2.gemfile"
163
+ - ".gitignore"
164
+ - ".rspec"
165
+ - ".travis.yml"
166
+ - Gemfile
167
+ - LICENSE
168
+ - README.md
169
+ - Rakefile
170
+ - lib/store_schema.rb
171
+ - lib/store_schema/accessor_definer.rb
172
+ - lib/store_schema/configuration.rb
173
+ - lib/store_schema/converter.rb
174
+ - lib/store_schema/converter/base.rb
175
+ - lib/store_schema/converter/boolean.rb
176
+ - lib/store_schema/converter/date_time.rb
177
+ - lib/store_schema/converter/float.rb
178
+ - lib/store_schema/converter/integer.rb
179
+ - lib/store_schema/converter/string.rb
180
+ - lib/store_schema/module.rb
181
+ - lib/store_schema/version.rb
182
+ - spec/lib/casted_store_accessor_spec.rb
183
+ - spec/spec_helper.rb
184
+ - spec/support/db.rb
185
+ - spec/support/env.rb
186
+ - spec/support/models.rb
187
+ - store_schema.gemspec
188
+ homepage: http://meskyanichi.github.io/store_schema/
189
+ licenses:
190
+ - MIT
191
+ metadata: {}
192
+ post_install_message:
193
+ rdoc_options: []
194
+ require_paths:
195
+ - lib
196
+ required_ruby_version: !ruby/object:Gem::Requirement
197
+ requirements:
198
+ - - ">="
199
+ - !ruby/object:Gem::Version
200
+ version: '0'
201
+ required_rubygems_version: !ruby/object:Gem::Requirement
202
+ requirements:
203
+ - - ">="
204
+ - !ruby/object:Gem::Version
205
+ version: '0'
206
+ requirements: []
207
+ rubyforge_project:
208
+ rubygems_version: 2.4.1
209
+ signing_key:
210
+ specification_version: 4
211
+ summary: Enhances ActiveRecord::Base.store_accessor with data conversion capabilities.
212
+ test_files:
213
+ - spec/lib/casted_store_accessor_spec.rb
214
+ - spec/spec_helper.rb
215
+ - spec/support/db.rb
216
+ - spec/support/env.rb
217
+ - spec/support/models.rb
218
+ has_rdoc: