store_schema 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: 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: