db_serializer 0.1.0

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
+ 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: []