db_serializer 0.1.0

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
+ SHA256:
3
+ metadata.gz: 455c86988e110584438e1edbbf8ef7c894e5a32ca6f69f49644d01e35cf9553a
4
+ data.tar.gz: c2b95f504f522e38992fe027f72cae332c8e7c96017ea1287fb9de6fab5b3b1a
5
+ SHA512:
6
+ metadata.gz: e35adc11a93293124fe2f39be8aee789fd1b02ddad3028cc111aad698e58d9d28a501870ffb2ab7381948d995fcfe0c6c49ab72951fc26b15310021453b042cd
7
+ data.tar.gz: 30ad4440828d712480b39b1588502ba5aebfc1a724f0e53661072670dc515e3ae8ea4a891d490479cb15193ddf196d0c0510c5d3b120bee9a734e611f9e6a969
data/README.md ADDED
@@ -0,0 +1,71 @@
1
+ # Db Serializer
2
+
3
+ This gem provide a blazing fast way to serialize Active Record models.
4
+ At the moment only a GeoJSON serializer is implemented
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ ```ruby
11
+ gem 'db_serializer'
12
+ ```
13
+
14
+ And then execute:
15
+
16
+ ```bash
17
+ $ bundle install
18
+ ```
19
+
20
+ Then you need to include the concern `DbSerializer::GeoJSON` to your model:
21
+
22
+ ```ruby
23
+ # In this example the City model has:
24
+ # - an attribute name of type string
25
+ # - an attribute boundaries of type geometry
26
+ class City < ActiveRecord::Base
27
+ # Include this concern
28
+ include DbSerializer::JSON
29
+
30
+ # Specify which column contains the geometry
31
+ # If you don't specify it, by default it will be :geometry
32
+ db_serializer :boundaries
33
+ end
34
+ ```
35
+
36
+ ## Usage
37
+
38
+ ```ruby
39
+ puts City.all.to_geojson([:id, :name])
40
+
41
+ # If you had such a model, it would output something like this:
42
+ # {
43
+ # "type": "FeatureCollection", "features": [{
44
+ # "id": 1, "type": "Feature", "geometry": {
45
+ # "type": "MultiLineString", "coordinates": [[[x1,y1],[x2,y2]]]
46
+ # },
47
+ # "properties": {"id": 1, "name": "Paris"}
48
+ # }]
49
+ # }
50
+ ```
51
+ Note: the output has been pretty printed for readability but please keep in mind that `to_geojson` returns a *string* containing a valid serialized JSON.
52
+ It means that you do not need to use `to_json` on it and that it can be parse with `JSON.parse`.
53
+
54
+ ## Development
55
+
56
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
57
+
58
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
59
+
60
+ ## Contributing
61
+
62
+ Bug reports and pull requests are welcome on GitHub at https://github.com/leonard-henriquez/db_serializer. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/leonard-henriquez/db_serializer/blob/master/CODE_OF_CONDUCT.md).
63
+
64
+
65
+ ## License
66
+
67
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
68
+
69
+ ## Code of Conduct
70
+
71
+ Everyone interacting in the db_serializer project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/leonard-henriquez/db_serializer/blob/master/CODE_OF_CONDUCT.md).
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support'
4
+ require 'active_record'
5
+
6
+ ##
7
+ # Module that contains spatial serializers for ActiveRecord models.
8
+ # The extended class must be an ActiveRecord::Base.
9
+ # It must also have a geometry column (the name of this column can be customized).
10
+ module DbSerializer
11
+ extend ActiveSupport::Autoload
12
+
13
+ autoload :Version
14
+ autoload :GeoJSON
15
+ autoload :Utilities
16
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DbSerializer
4
+ # JSON Serializer
5
+ module GeoJSON
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ @db_serializer_options = {
10
+ field: :geometry
11
+ }
12
+
13
+ private
14
+
15
+ ##
16
+ # Adds a geojson attribute to the current ActiveRecord::Relation
17
+ # @param columns [Array<Symbol, String>] list of the columns to include in the GeoJSON
18
+ # @return [ActiveRecord::Relation] relation with a new attribute named geojson
19
+ scope :set_geojson_attribute, ->(columns = nil) do
20
+ columns = select_values if columns.nil?
21
+ params = {
22
+ geometry_column: db_serializer_options[:field],
23
+ field_name: :geojson
24
+ }
25
+
26
+ attribute = DbSerializer::Utilities::SQL.geojson_attribute(columns, params)
27
+
28
+ _select!(attribute)
29
+ end
30
+ end
31
+
32
+ class_methods do
33
+ ##
34
+ # Allows to set the options for this gem
35
+ # @param field [Symbol] name of the attribute containing geometry
36
+ # @return [Hash]
37
+ def db_serializer(field = :geometry)
38
+ @db_serializer_options = {}
39
+ @db_serializer_options[:field] = field.to_sym
40
+ db_serializer_options
41
+ end
42
+
43
+ ##
44
+ # Allows to get the options for this gem
45
+ # @return [Hash] contains keys :field
46
+ def db_serializer_options
47
+ if defined?(@db_serializer_options)
48
+ @db_serializer_options
49
+ elsif superclass.respond_to?(:db_serializer_options)
50
+ superclass.db_serializer_options || {}
51
+ else
52
+ {}
53
+ end
54
+ end
55
+
56
+ ##
57
+ # Creates a json serialized FeatureCollection of the ActiveRecord::Relation.
58
+ #
59
+ # FeatureCollection specs: http://wiki.geojson.org/GeoJSON_draft_version_6#FeatureCollection
60
+ #
61
+ # It is already serialized so there is no need to apply +.to_json+.
62
+ #
63
+ # @param columns [Array<Symbol, String>]
64
+ # @return [String] JSON serialized FeatureCollection
65
+ def to_geojson(columns = nil)
66
+ features = set_geojson_attribute(columns)
67
+ query = DbSerializer::Utilities::SQL.feature_collection(features)
68
+ ActiveRecord::Base.connection.query_value(query)
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,125 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DbSerializer
4
+ ##
5
+ # Module defining geographic tools
6
+ module Utilities
7
+ ##
8
+ # SQL utilities for spatial queries
9
+ module SQL
10
+ class << self
11
+ ##
12
+ # This method returns a string containing a SQL query to build a GeoJSON FeatureCollection.
13
+ #
14
+ # The result of the SQL query generated by this method contains one row with one
15
+ # column. It contains a serialized GeoJSON FeatureCollection.
16
+ #
17
+ # Example of GeoJSON FeatureCollection:
18
+ # <code>
19
+ # {"type": "FeatureCollection", "features": [{"id": 1, "type":
20
+ # "Feature", "geometry": {"type": "MultiLineString", "coordinates": [[[x,y]]]},
21
+ # "properties": {"id": 1, "name": "Paris"}}]}
22
+ # </code>
23
+ #
24
+ # To learn more about FeatureCollections see the specifications:
25
+ # http://wiki.geojson.org/GeoJSON_draft_version_6#FeatureCollection
26
+ #
27
+ #
28
+ # @example
29
+ # features = MyActiveRecordModel.select(geojson_attribute([:id]))
30
+ # query = feature_collection(features)
31
+ # ActiveRecord::Base.connection.query_value(query)
32
+ #
33
+ # @param features [String, ActiveRecord::Relation] query of the features to include
34
+ # @param opts [Hash]
35
+ # @option opts [String, Symbol] :geojson_column name of the column containing
36
+ # the GeoJSON features to include in the collection
37
+ # @option opts [String, Symbol] :output_column name of the output column
38
+ # @return [String] SQL to get a FeatureCollection
39
+ def feature_collection(features, opts = {})
40
+ opts = {
41
+ geojson_column: :geojson,
42
+ output_column: :json
43
+ }.merge(opts)
44
+
45
+ geojson_column = opts[:geojson_column]
46
+ output_column = opts[:output_column]
47
+ inner_query = features.is_a?(String) ? features : features.to_sql
48
+
49
+ <<-SQL
50
+ SELECT jsonb_build_object(
51
+ 'type', 'FeatureCollection',
52
+ 'features', COALESCE(jsonb_agg(features.#{geojson_column}), '[]')
53
+ ) AS #{output_column} FROM (#{inner_query}) features
54
+ SQL
55
+ end
56
+
57
+ ##
58
+ # This method returns a string containing part of a SQL query that can be used
59
+ # in a +SELECT+ statement to add a GeoJSON feature column to a query.
60
+ #
61
+ # The SQL column generated by this method contains a serialized GeoJSON feature.
62
+ #
63
+ # Example of GeoJSON feature:
64
+ # <code>
65
+ # {"id": 1, "type": "Feature", "geometry":
66
+ # {"type": "MultiLineString", "coordinates": [[[x,y]]]},
67
+ # "properties": {"id": 1, "name": "Paris"}}
68
+ # </code>
69
+ #
70
+ # To learn more about GeoJSON features see the specifications:
71
+ # http://wiki.geojson.org/GeoJSON_draft_version_6#Feature
72
+ #
73
+ # @example with SQL:
74
+ # geojson = DbSerializer::Utilities::SQL.geojson_attribute([:id])
75
+ # records = ActiveRecord::Base.connection.exec_query(
76
+ # "SELECT id, geometry, #{geojson} FROM table"
77
+ # )
78
+ # record = records.first
79
+ #
80
+ # # print a String containing a serialized GeoJSON feature, check example in the note
81
+ # puts record['geojson']
82
+ #
83
+ # @example with an +ActiveRecord::Base+
84
+ # geojson = DbSerializer::Utilities::SQL.geojson_attribute([:id])
85
+ # records = Model.select(geojson)
86
+ # record = records.first
87
+ #
88
+ # # print a String containing a serialized GeoJSON feature, check example in the note
89
+ # puts record.geojson_before_type_cast
90
+ #
91
+ # # print a Hash
92
+ # puts record.geojson
93
+ #
94
+ # @param columns [Array<String, Symbol>] SQL columns to include in the properties
95
+ # of the GeoJSON feature, must be accessible in the underlying table
96
+ # @param opts [Hash]
97
+ # @option opts [Symbol] :geometry_column defaults to :geometry
98
+ # @option opts [Symbol] :output_column defaults to :geojson
99
+ # @return [String] SQL +SELECT+ statement to add a column named +geojson+ to a SQL query
100
+ def geojson_attribute(columns, opts = {})
101
+ opts = {
102
+ geometry_column: :geometry,
103
+ output_column: :geojson
104
+ }.merge(opts)
105
+
106
+ filtered_columns = columns.reject { |column| column.to_s == opts[:geometry_column].to_s }
107
+ columns = filtered_columns.map { |column| "'#{column}',#{column}" }.join(',')
108
+
109
+ geometry = "ST_AsGeoJSON(#{opts[:geometry_column]})::jsonb"
110
+ properties = "json_build_object(#{columns})"
111
+ output_column = opts[:output_column]
112
+
113
+ <<-SQL
114
+ jsonb_build_object(
115
+ 'type', 'Feature',
116
+ 'id', id,
117
+ 'geometry', #{geometry},
118
+ 'properties', #{properties}
119
+ ) AS #{output_column}
120
+ SQL
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DbSerializer
4
+ VERSION = '0.1.0'
5
+ end
metadata ADDED
@@ -0,0 +1,79 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: db_serializer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Leonard Henriquez
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-05-09 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: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activerecord-postgis-adapter
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
+ description: Add geometry features on Active Record models
42
+ email:
43
+ - leonard@flatlooker.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - README.md
49
+ - lib/db_serializer.rb
50
+ - lib/db_serializer/geo_json.rb
51
+ - lib/db_serializer/utilities.rb
52
+ - lib/db_serializer/version.rb
53
+ homepage: https://github.com/Flatlooker/db_serializer
54
+ licenses:
55
+ - MIT
56
+ metadata:
57
+ homepage_uri: https://github.com/Flatlooker/db_serializer
58
+ source_code_uri: https://github.com/Flatlooker/db_serializer.git
59
+ changelog_uri: https://github.com/Flatlooker/db_serializer
60
+ post_install_message:
61
+ rdoc_options: []
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: 2.3.0
69
+ required_rubygems_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ requirements: []
75
+ rubygems_version: 3.1.3
76
+ signing_key:
77
+ specification_version: 4
78
+ summary: Add geometry features on Active Record models
79
+ test_files: []