lazy_as_json 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2089ff5725a076fc31c149b22365a0c661c517ba
4
+ data.tar.gz: 8c018d3b697b855eda8d922fa4fe8f83ba11dee0
5
+ SHA512:
6
+ metadata.gz: 42a68a54d003a1863aff93dd434dca60b17c0390d4783e282a7d4a5309eb67934dd12b8807493d12dab3831a9c433035badb45bdf6f8f3af1af1d3eb7a5283de
7
+ data.tar.gz: 212626600b580f4ff5fbeb4e62f879c5fcf71094c9585434fcc72758824db30c9ed3108292f81d800c4c086ad8d523b00f8dc7e4e65cc84dc773e52b836a8500
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ /.idea/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.1
4
+ before_install: gem install bundler -v 1.11.2
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'bundler', '~> 1.11.2'
4
+
5
+ # Specify your gem's dependencies in lazy_as_json.gemspec
6
+ gemspec
@@ -0,0 +1,72 @@
1
+ # LazyAsJson
2
+
3
+ [![Circle CI](https://circleci.com/gh/we4tech/lazy-as-json.svg?style=svg)](https://circleci.com/gh/we4tech/lazy-as-json)
4
+
5
+ A simple and concise way to use as_json with “only”, “except” and other options without using them literally.
6
+
7
+ Instead of using this -
8
+
9
+ `User.as_json(only: [:id, :first_name, profiles: [:company, :location]])`
10
+
11
+ You can perhaps use this -
12
+
13
+ `User.as_json(only_keys: ‘_,first_name,profiles(p),p.company,p.location’)`
14
+
15
+ As simple as this.
16
+
17
+ You can control what your API response should include through a flexible parameter string.
18
+
19
+ i.e. - “/api/v1/users/me?_keys=_,last_name,profiles(p),p.company,p.location”
20
+
21
+ This parameter string could dig through the nested objects and their nesting too.
22
+ Just to reduce the API response size significantly, you can use this parameter control over wherever it is used.
23
+ However it might seems quite trivial but frankly speaking it saves lot in response data hence faster loading time at client side.
24
+
25
+ Moreover as it uses Hash.new and constructs attribute on runtime, you can throttle calling from the expensive method by using this parameter string.
26
+
27
+ ## How to Construct Attribute Filters ?
28
+
29
+ |Symbol|Description|
30
+ |---|---|
31
+ |_|It represents id as short cut|
32
+ |nested_objects(< name >)|It represents the alias for the nested object (it works with single or multiple values)|
33
+ |< alias_name >.< key >|It represents the specific attribute from the nested object|
34
+ |< alias_name >.< key >(< alias >)|It represents aliasing a nested object from a nested object and this list could go on and on.|
35
+
36
+ ### Example Usages
37
+
38
+ ```ruby
39
+ User.as_json(only_keys: '_,first_name,email,profiles(p),p._,p.company,p.address(pa),pa.city,pa.country(pac),pac.iso_code')
40
+ ```
41
+
42
+ Here, "_" represents `user_instance.id`, `profiles` represents `user_instance.profiles`, `p.address(pa)` represents `user_instance.profiles[].address` and finally `pa.country(pac)` represents `user_instance.profiles[].address.country` and so on.
43
+
44
+ ## Installation
45
+
46
+ Add this line to your application's Gemfile:
47
+
48
+ ```ruby
49
+ gem 'lazy_as_json'
50
+ ```
51
+
52
+ And then execute:
53
+
54
+ $ bundle
55
+
56
+ Or install it yourself as:
57
+
58
+ $ gem install lazy_as_json
59
+
60
+ ## Usage
61
+
62
+ TODO: Write usage instructions here
63
+
64
+ ## Development
65
+
66
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
67
+
68
+ 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).
69
+
70
+ ## Contributing
71
+
72
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/lazy_as_json.
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "lazy_as_json"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,8 @@
1
+ machine:
2
+ ruby:
3
+ version: 2.2.1
4
+
5
+ dependencies:
6
+ pre:
7
+ - if $(gem list bundler -v 1.9.5 -i); then gem uninstall -x -i /home/ubuntu/.rvm/gems/ruby-2.2.1@global bundler; fi
8
+ - gem install bundler -v "1.11.2"
@@ -0,0 +1,55 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'lazy_as_json/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'lazy_as_json'
8
+ spec.version = LazyAsJson::VERSION
9
+ spec.authors = ['nhm tanveer hossain khan']
10
+ spec.email = ['hasan83bd@gmail.com']
11
+
12
+ spec.summary = %q{Take control on what to return from the API response, define the attributes map in a short syntax over parameter}
13
+ spec.description = %q{Lazy As Json
14
+
15
+ A simple and concise way to use as_json with “only”, “except” and other options without using them literally.
16
+
17
+ Instead of using this -
18
+
19
+ `User.as_json(only: [:id, :first_name, profiles: [:company, :location]])`
20
+
21
+ You can perhaps use this -
22
+
23
+ `User.as_json(only_keys: ‘_,first_name,profiles(p),p.company,p.location’)`
24
+
25
+ As simple as this.
26
+
27
+ You can control what your API response should include through a flexible parameter string.
28
+
29
+ i.e. - “/api/v1/users/me?_keys=_,last_name,profiles(p),p.company,p.location”
30
+
31
+ This parameter string could dig through the nested objects and their nesting too.
32
+ Just to reduce the API response size significantly, you can use this parameter control over wherever it is used.
33
+ However it might seems quite trivial but frankly speaking it saves lot in response data hence faster loading time at client side.
34
+
35
+ Moreover as it uses Hash.new and constructs attribute on runtime, you can throttle calling from the expensive method by using this parameter string.
36
+ }
37
+ spec.homepage = 'http://hasan.wordpress.com'
38
+
39
+ # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
40
+ # delete this section to allow pushing this gem to any host.
41
+ if spec.respond_to?(:metadata)
42
+ spec.metadata['allowed_push_host'] = "https://rubygems.org"
43
+ else
44
+ raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
45
+ end
46
+
47
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
48
+ spec.bindir = "exe"
49
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
50
+ spec.require_paths = ["lib"]
51
+
52
+ spec.add_development_dependency "bundler", "~> 1.11"
53
+ spec.add_development_dependency "rake", "~> 10.0"
54
+ spec.add_development_dependency "rspec", "~> 3.0"
55
+ end
@@ -0,0 +1,5 @@
1
+ require 'lazy_as_json/version'
2
+
3
+ module LazyAsJson ; end
4
+
5
+ require 'lazy_as_json/attribute_filter'
@@ -0,0 +1,160 @@
1
+ module LazyAsJson
2
+ module AttributeFilter
3
+
4
+ def self.included(base)
5
+ base.respond_to?(:prepend) ?
6
+ base.prepend(OnlyKeysFilter) :
7
+ base.include(OnlyKeysFilter)
8
+ end
9
+
10
+ module OnlyKeysFilter
11
+ # The list of excluded attribute accessor name
12
+ BANNED_KEYS = [:all, :delete, :delete_all, :destroy_all, :destroy, :update, :save, :create,
13
+ :create!, :save!, :update_attributes, :update_attribute, :update_attributes!,
14
+ :find, :where, :increment, :increment!, :decrement, :decrement!, :remove,
15
+ :object_id]
16
+
17
+ # Returns the JSON serializable hash object with the data from the specified keys.
18
+ #
19
+ # It Looks up for :only_keys for enabling attribute throttling, in case of absense it returns the
20
+ # actual data from overrode 'as_json' method.
21
+ #
22
+ # @param opts [Hash] The additional options whether to throttle response attribute or not.
23
+ # @return [Hash] The generated has based on the trottled keys or based on the overrode as_json method
24
+ def as_json(opts = {})
25
+ __set_only_keys opts.delete(:only_keys)
26
+
27
+ __lazy_mode? ? build_as_json : super(opts)
28
+ end
29
+
30
+ # Sets the only keys, if you intend to use those directly instead of going through :as_json
31
+ #
32
+ # @return key_str [String] The attribute filtering string
33
+ def __set_only_keys(key_str)
34
+ @__only_keys = key_str.to_s.strip
35
+ @__only_keys = @__only_keys.empty? ? nil : @__only_keys
36
+ end
37
+
38
+ # Returns the applied attribute filtering string
39
+ #
40
+ # @return [String] The attribute filtering string
41
+ def __only_keys
42
+ @__only_keys
43
+ end
44
+
45
+ # Creates a Hash object which lazly assembles attributes from the object or any of their parents.
46
+ # However, it could be delegated from another object through exposing :source method.
47
+ #
48
+ # @return [Hash] The empty Hash object with the attribute builder closure
49
+ def lazy_as_json(opts = {})
50
+ Hash.new do |hash, key|
51
+ hash[key] = _with_lazy(_find_value(key))
52
+ end
53
+ end
54
+
55
+ # Returns true if the lazy mode is enabled through :only_keys option
56
+ #
57
+ # @return [Boolean] true if lazy mode is enabled
58
+ def __lazy_mode?
59
+ __only_keys && !__only_keys.empty?
60
+ end
61
+
62
+ # Builds the attributes hash object based on the applied attribute filtering string
63
+ #
64
+ # @return [Hash] The attributes based on the attribute filtering string
65
+ def build_as_json
66
+ @_af_references = {}
67
+ hash = lazy_as_json
68
+ keys = []
69
+
70
+ __only_keys.split(',').each do |key|
71
+ value = hash
72
+
73
+ for nested_key in find_full_key(key)
74
+ if Array === value
75
+ value = value.map { |v| v[nested_key] }.compact
76
+ elsif Hash === value
77
+ value = value[nested_key]
78
+ end
79
+ end
80
+ end
81
+
82
+ hash
83
+ end
84
+
85
+ # Return true if the specified attribute key is not banned
86
+ #
87
+ # @return [Boolean] True if not listed under *BANNED_KEYS*
88
+ def __allowed_attribute_key?(key)
89
+ !BANNED_KEYS.include?(key)
90
+ end
91
+
92
+ private
93
+
94
+ def _with_lazy(value)
95
+ if value.respond_to?(:lazy_as_json)
96
+ value.lazy_as_json
97
+ elsif Array === value
98
+ value.map { |v| v.respond_to?(:lazy_as_json) ? v.lazy_as_json : v }
99
+ else
100
+ value
101
+ end
102
+ end
103
+
104
+ def _find_value(key)
105
+ return unless __allowed_attribute_key?(key)
106
+
107
+ if respond_to?(key)
108
+ send(key)
109
+ elsif _source_declared? && source.respond_to?(key)
110
+ source.send(key)
111
+ end
112
+ end
113
+
114
+ def _source_declared?
115
+ @_source_declared ||= respond_to?(:source)
116
+ end
117
+
118
+ # Generate a fully qualified key based on it's parent object reference tree based
119
+ # on the shortest key name.
120
+ #
121
+ def find_full_key(key)
122
+ # Try to find 'actual_name(alias)' key formation
123
+ if (matched = key.match(/^(.+)\((\w+)\)$/))
124
+ prefixes = matched[1].split('.')
125
+ @_af_references[matched[2]] =
126
+ (wrap_array(prefixes[0, prefixes.size - 1]).
127
+ map { |prefix| @_af_references[prefix] } << prefixes.last.to_sym).
128
+ flatten
129
+ else
130
+ # Split key string based on the '.'
131
+ parts = key.split('.')
132
+
133
+ # Traverse through all prefixes and try to find if those are aliased or not aliased
134
+ # and build a complete reference tree.
135
+ prefixes = parts[0, parts.size - 1].map { |prefix| @_af_references[prefix] || [] }.flatten
136
+
137
+ # Take the actual key part
138
+ key_part = parts.last
139
+
140
+ # Construct the full reference tree and expand if short form is used.
141
+ suffix = if key_part == '_'
142
+ :id
143
+ elsif (matched = key_part.match(/^(.+)_$/))
144
+ :"#{matched[1]}_id"
145
+ else
146
+ key_part.to_sym
147
+ end
148
+
149
+ prefixes << suffix
150
+ end
151
+ end
152
+
153
+ def wrap_array(values)
154
+ return [] if values.nil?
155
+ return values if Array === values
156
+ [values]
157
+ end
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,3 @@
1
+ module LazyAsJson
2
+ VERSION = "0.1.0"
3
+ end
metadata ADDED
@@ -0,0 +1,123 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lazy_as_json
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - nhm tanveer hossain khan
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-03-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.11'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.11'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ description: |
56
+ Lazy As Json
57
+
58
+ A simple and concise way to use as_json with “only”, “except” and other options without using them literally.
59
+
60
+ Instead of using this -
61
+
62
+ `User.as_json(only: [:id, :first_name, profiles: [:company, :location]])`
63
+
64
+ You can perhaps use this -
65
+
66
+ `User.as_json(only_keys: ‘_,first_name,profiles(p),p.company,p.location’)`
67
+
68
+ As simple as this.
69
+
70
+ You can control what your API response should include through a flexible parameter string.
71
+
72
+ i.e. - “/api/v1/users/me?_keys=_,last_name,profiles(p),p.company,p.location”
73
+
74
+ This parameter string could dig through the nested objects and their nesting too.
75
+ Just to reduce the API response size significantly, you can use this parameter control over wherever it is used.
76
+ However it might seems quite trivial but frankly speaking it saves lot in response data hence faster loading time at client side.
77
+
78
+ Moreover as it uses Hash.new and constructs attribute on runtime, you can throttle calling from the expensive method by using this parameter string.
79
+ email:
80
+ - hasan83bd@gmail.com
81
+ executables: []
82
+ extensions: []
83
+ extra_rdoc_files: []
84
+ files:
85
+ - ".gitignore"
86
+ - ".rspec"
87
+ - ".travis.yml"
88
+ - Gemfile
89
+ - README.md
90
+ - Rakefile
91
+ - bin/console
92
+ - bin/setup
93
+ - circle.yml
94
+ - lazy_as_json.gemspec
95
+ - lib/lazy_as_json.rb
96
+ - lib/lazy_as_json/attribute_filter.rb
97
+ - lib/lazy_as_json/version.rb
98
+ homepage: http://hasan.wordpress.com
99
+ licenses: []
100
+ metadata:
101
+ allowed_push_host: https://rubygems.org
102
+ post_install_message:
103
+ rdoc_options: []
104
+ require_paths:
105
+ - lib
106
+ required_ruby_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ required_rubygems_version: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ version: '0'
116
+ requirements: []
117
+ rubyforge_project:
118
+ rubygems_version: 2.5.1
119
+ signing_key:
120
+ specification_version: 4
121
+ summary: Take control on what to return from the API response, define the attributes
122
+ map in a short syntax over parameter
123
+ test_files: []