webmate 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. data/.gitignore +19 -0
  2. data/GUIDE.md +115 -0
  3. data/Gemfile +10 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +47 -0
  6. data/Rakefile +1 -0
  7. data/bin/webmate +70 -0
  8. data/lib/webmate/application.rb +138 -0
  9. data/lib/webmate/config.rb +23 -0
  10. data/lib/webmate/decorators/base.rb +38 -0
  11. data/lib/webmate/env.rb +17 -0
  12. data/lib/webmate/logger.rb +20 -0
  13. data/lib/webmate/observers/base.rb +24 -0
  14. data/lib/webmate/presenters/base.rb +69 -0
  15. data/lib/webmate/presenters/base_presenter.rb +115 -0
  16. data/lib/webmate/presenters/scoped.rb +30 -0
  17. data/lib/webmate/responders/abstract.rb +127 -0
  18. data/lib/webmate/responders/base.rb +21 -0
  19. data/lib/webmate/responders/callbacks.rb +79 -0
  20. data/lib/webmate/responders/exceptions.rb +4 -0
  21. data/lib/webmate/responders/response.rb +36 -0
  22. data/lib/webmate/responders/templates.rb +65 -0
  23. data/lib/webmate/route_helpers/route.rb +91 -0
  24. data/lib/webmate/route_helpers/routes_collection.rb +273 -0
  25. data/lib/webmate/socket.io/actions/connection.rb +14 -0
  26. data/lib/webmate/socket.io/actions/handshake.rb +34 -0
  27. data/lib/webmate/socket.io/packets/ack.rb +5 -0
  28. data/lib/webmate/socket.io/packets/base.rb +156 -0
  29. data/lib/webmate/socket.io/packets/connect.rb +5 -0
  30. data/lib/webmate/socket.io/packets/disconnect.rb +5 -0
  31. data/lib/webmate/socket.io/packets/error.rb +5 -0
  32. data/lib/webmate/socket.io/packets/event.rb +5 -0
  33. data/lib/webmate/socket.io/packets/heartbeat.rb +5 -0
  34. data/lib/webmate/socket.io/packets/json.rb +5 -0
  35. data/lib/webmate/socket.io/packets/message.rb +5 -0
  36. data/lib/webmate/socket.io/packets/noop.rb +5 -0
  37. data/lib/webmate/support/em_mongoid.rb +53 -0
  38. data/lib/webmate/version.rb +3 -0
  39. data/lib/webmate/views/scope.rb +25 -0
  40. data/lib/webmate/websockets.rb +50 -0
  41. data/lib/webmate.rb +129 -0
  42. data/spec/lib/route_helpers/route_spec.rb +41 -0
  43. data/spec/spec_helper.rb +18 -0
  44. data/vendor/.DS_Store +0 -0
  45. data/vendor/assets/.DS_Store +0 -0
  46. data/vendor/assets/javascripts/.DS_Store +0 -0
  47. data/vendor/assets/javascripts/webmate/.DS_Store +0 -0
  48. data/vendor/assets/javascripts/webmate/auth.coffee +63 -0
  49. data/vendor/assets/javascripts/webmate/backbone_ext/.DS_Store +0 -0
  50. data/vendor/assets/javascripts/webmate/backbone_ext/resources.coffee +60 -0
  51. data/vendor/assets/javascripts/webmate/backbone_ext/sync.coffee +131 -0
  52. data/vendor/assets/javascripts/webmate/client.coffee +133 -0
  53. data/vendor/assets/javascripts/webmate/init.coffee +7 -0
  54. data/vendor/assets/javascripts/webmate/libs/.DS_Store +0 -0
  55. data/vendor/assets/javascripts/webmate/libs/backbone.js +1572 -0
  56. data/vendor/assets/javascripts/webmate/libs/benchmark.coffee +27 -0
  57. data/vendor/assets/javascripts/webmate/libs/icanhaz.js +542 -0
  58. data/vendor/assets/javascripts/webmate/libs/socket.io.js +3871 -0
  59. data/vendor/assets/javascripts/webmate/libs/underscore.js +1 -0
  60. data/vendor/assets/javascripts/webmate.js +10 -0
  61. data/webmate.gemspec +31 -0
  62. metadata +290 -0
data/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ .rspec
7
+ .ruby-version
8
+ Gemfile.lock
9
+ InstalledFiles
10
+ _yardoc
11
+ coverage
12
+ doc/
13
+ lib/bundler/man
14
+ pkg
15
+ rdoc
16
+ spec/reports
17
+ test/tmp
18
+ test/version_tmp
19
+ tmp
data/GUIDE.md ADDED
@@ -0,0 +1,115 @@
1
+ #webmate app skeleton
2
+ see working example on https://github.com/evgeniypetrov/webmate-app-skeleton
3
+
4
+ ## Quick start
5
+
6
+ to start application, run
7
+ webmate start
8
+ application will be available on localhost:3000 url
9
+
10
+ to run irb/console
11
+ webmate console
12
+
13
+ type webmate without params to know all options
14
+
15
+ show all available routes [ without assets ]
16
+ rake routes k
17
+
18
+ ## Tutorial
19
+ ### Skeleton
20
+ require files
21
+
22
+ to create file, add following gem to Gemfile
23
+
24
+ Gemfile
25
+ gem 'webmate'
26
+ gem 'slim'
27
+ gem 'sass', group: assets
28
+ gem 'alphasights-sinatra-sprockets', require: 'sinatra-sprockets', group: assets
29
+
30
+ base required files:
31
+
32
+ config.ru
33
+ require './config/environment'
34
+ if configatron.assets.compile
35
+ map '/assets' do
36
+ run Sinatra::Sprockets.environment
37
+ end
38
+ end
39
+ run ExampleApp
40
+
41
+ config/config.rb
42
+ Webmate::Application.configure do |config|
43
+ # add directory to application load paths
44
+ #config.app.load_paths << ["app/uploaders"]
45
+ config.app.cache_classes = true
46
+ config.assets.compile = false
47
+
48
+ config.websockets.namespace = 'api'
49
+ config.websockets.enabled = true
50
+ config.websockets.port = 9020
51
+ end
52
+
53
+ Webmate::Application.configure(:development) do |config|
54
+ config.app.cache_classes = false
55
+ config.assets.compile = true
56
+ config.websockets.port = 3503
57
+ end
58
+
59
+ config/application.rb
60
+ require 'digest/sha1'
61
+ require 'base64'
62
+
63
+ class ExampleApp < Webmate::Application
64
+ # do other things)
65
+ end
66
+
67
+ config/environment.rb
68
+ WEBMATE_ROOT = File.expand_path('.')
69
+ require 'webmate'
70
+
71
+ Dir[File.join(WEBMATE_ROOT, 'app', 'routes', '**', '*.rb')].each do |file|
72
+ require file
73
+ end
74
+
75
+ ### Hello world
76
+ adding route
77
+ app/routes/facade_routes.rb
78
+ ExampleApp.define_routes do
79
+ get '/', to: 'pages#index', transport: [:http]
80
+ end
81
+
82
+ adding responder to this route
83
+ create files in app/responders
84
+ base_responder
85
+ class BaseResponder < Webmate::Responders::Base
86
+ # Available options
87
+ # before_filter :do_something
88
+
89
+ rescue_from Webmate::Responders::ActionNotFound do
90
+ render_not_found
91
+ end
92
+ end
93
+
94
+ pages_responder
95
+ class PagesResponder < BaseResponder
96
+ include Webmate::Responders::Templates
97
+
98
+ def index
99
+ slim :index, layout: 'application'
100
+ end
101
+ end
102
+
103
+ also, required files
104
+ app/views/layouts/application.html.slim
105
+ app/views/pages/index.html.slim
106
+
107
+ ### Assets
108
+ assets will be searched in app/assets folder, so place them to
109
+ app/assets/javascripts
110
+ app/assets/stylesheets
111
+
112
+ ### Utilities - rake task, scripts
113
+ Rakefile
114
+ require File.expand_path('../config/environment', __FILE__)
115
+ Webmate::Application.load_tasks
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in webmate.gemspec
4
+ gemspec
5
+
6
+ group :development do
7
+ gem 'rake'
8
+ gem 'rspec'
9
+ gem 'webmate-sprockets'
10
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Iskander Haziev
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,47 @@
1
+ # Webmate
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Prerequisites
6
+ git
7
+ mongo
8
+ redis
9
+
10
+ sinatra 4.2
11
+ to make this, use sinatra-contrib at least 1.4.0 version
12
+ Specify in your Gemfile
13
+ gem 'sinatra-contrib', git: 'git://github.com/sinatra/sinatra-contrib.git'
14
+
15
+ ## Installation
16
+
17
+ Add this line to your application's Gemfile:
18
+
19
+ gem 'webmate'
20
+
21
+ And then execute:
22
+
23
+ $ bundle
24
+
25
+ Or install it yourself as:
26
+
27
+ $ gem install webmate
28
+
29
+ ## Usage
30
+
31
+ TODO: Write usage instructions here
32
+
33
+ start server as
34
+ webmate server [-p port]
35
+
36
+ running interactive console
37
+ webmate console [environment]
38
+ or
39
+ webmate console [-e environment]
40
+
41
+ ## Contributing
42
+
43
+ 1. Fork it
44
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
45
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
46
+ 4. Push to the branch (`git push origin my-new-feature`)
47
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/webmate ADDED
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- encoding: utf-8 -*-
3
+
4
+ require 'optparse'
5
+ require 'ostruct'
6
+
7
+ options = OpenStruct.new(
8
+ port: 3000,
9
+ environment: 'development',
10
+ daemon: false,
11
+ rackup: 'config.ru'
12
+ )
13
+
14
+ opt_parser = OptionParser.new do |opt|
15
+ opt.banner = "Usage: webmate COMMAND [OPTIONS]"
16
+ opt.separator ""
17
+ opt.separator "Commands"
18
+ opt.separator " server: start server"
19
+ opt.separator " console [environment]: application console"
20
+ opt.separator " generate [app_name]: do something useful"
21
+ opt.separator ""
22
+ opt.separator "Options"
23
+
24
+ opt.on("-e","--environment ENVIRONMENT","which environment you want to run") do |environment|
25
+ options.environment = environment
26
+ end
27
+
28
+ opt.on("-d","--daemon","daemon mode") do |daemon|
29
+ options.daemon = daemon
30
+ end
31
+
32
+ opt.on("-p","--port PORT", Integer, "port number") do |port|
33
+ options.port = port
34
+ end
35
+
36
+ opt.on("-R","--rackup FILE", "rackup file") do |file|
37
+ options.rackup_file = file
38
+ end
39
+ end
40
+
41
+ opt_parser.parse!
42
+
43
+ command = ARGV.shift
44
+ case command
45
+ when 'server'
46
+ #TODO refactor to
47
+ # require 'lib/commands/server'
48
+ # with options
49
+ require './config/environment'
50
+ require 'thin'
51
+
52
+ cmd = ["start"]
53
+ cmd += ["-p", options.port.to_s]
54
+
55
+ Thin::Runner.new(cmd).run!
56
+ when 'console'
57
+ require 'irb'
58
+ ENV["RACK_ENV"] ||= ARGV.first || options.environment || 'development'
59
+ puts "Running Webmate console with env: #{ENV["RACK_ENV"]}"
60
+ require './config/environment'
61
+ ARGV.clear
62
+ IRB.start
63
+
64
+ when 'generate'
65
+ puts 'not yet implemented'
66
+ puts opt_parser
67
+
68
+ else
69
+ puts opt_parser
70
+ end
@@ -0,0 +1,138 @@
1
+ module Webmate
2
+ class Application < Sinatra::Base
3
+ # override sinatra's method
4
+ def route!(base = settings, pass_block = nil)
5
+ transport = @request.websocket? ? 'WS' : 'HTTP'
6
+
7
+ route_info = base.routes.match(@request.request_method, transport, @request.path)
8
+
9
+ # no route case - use default sinatra's processors
10
+ if !route_info
11
+ route_eval(&pass_block) if pass_block
12
+ route_missing
13
+ end
14
+
15
+ if @request.websocket?
16
+ unless authorized_to_open_connection?(route_info[:params][:scope])
17
+ return [401, {}, []]
18
+ end
19
+
20
+ session_id = route_info[:params][:session_id].inspect
21
+ Webmate::Websockets.subscribe(session_id, @request) do |message|
22
+ if route_info = base.routes.match(message['method'], 'WS', message.path)
23
+ request_info = {
24
+ path: message.path,
25
+ metadata: message.metadata || {},
26
+ action: route_info[:action],
27
+ params: message.params.merge(route_info[:params]),
28
+ request: @request
29
+ }
30
+ # here we should create subscriber who can live
31
+ # between messages.. but not between requests.
32
+ response = route_info[:responder].new(request_info).respond
33
+
34
+ # result of block will be sent back to user
35
+ response
36
+ end
37
+ end
38
+
39
+ # this response not pass to user - so we keep connection alive.
40
+ # passing other response will close connection and socket
41
+ non_pass_response = [-1, {}, []]
42
+ return non_pass_response
43
+
44
+ else # HTTP
45
+ # this should return correct Rack response..
46
+ request_info = params_for_responder(route_info)
47
+ response = route_info[:responder].new(request_info).respond
48
+
49
+ return response.rack_format
50
+ end
51
+ end
52
+
53
+ # this method prepare data for responder
54
+ # {
55
+ # path: '/',
56
+ # metadata: {},
57
+ # action: 'index',
58
+ # params: { test: true }
59
+ # }
60
+ def params_for_responder(route_info)
61
+ # create unified request info
62
+ # request_info = { path: '/', metadata: {}, action: 'index', params: { test: true } }
63
+ request_params = parsed_request_params
64
+ metadata = request_params.delete(:metadata)
65
+ {
66
+ path: @request.path,
67
+ metadata: metadata || {},
68
+ action: route_info[:action],
69
+ params: request_params.merge(route_info[:params]),
70
+ request: @request
71
+ }
72
+ end
73
+
74
+ # @request.params working only for get params
75
+ # and params in url line ?key=value
76
+ def parsed_request_params
77
+ request_params = HashWithIndifferentAccess.new
78
+ request_params.merge!(@request.params || {})
79
+
80
+ # read post or put params. this will erase params
81
+ # {code: 123, mode: 123}
82
+ # "code=123&mode=123"
83
+ request_body = @request.body.read
84
+ if request_body.present?
85
+ body_params = begin
86
+ JSON.parse(request_body) # {code: 123, mode: 123}
87
+ rescue JSON::ParserError
88
+ Rack::Utils.parse_nested_query(request_body) # "code=123&mode=123"
89
+ end
90
+ else
91
+ body_params = {}
92
+ end
93
+
94
+ request_params.merge(body_params)
95
+ end
96
+
97
+ # update this method to create auth restrictions
98
+ def authorized_to_open_connection?(scope = :user)
99
+ return true
100
+ end
101
+
102
+ class << self
103
+ def configure(env = nil, &block)
104
+ if !env || Webmate.env?(env)
105
+ block.call(configatron)
106
+ end
107
+ end
108
+
109
+ def define_routes(&block)
110
+ settings = Webmate::Application
111
+ unless settings.routes.is_a?(RoutesCollection)
112
+ routes = RoutesCollection.new()
113
+ settings.set(:routes, routes)
114
+ end
115
+ settings.routes.define_routes(&block)
116
+
117
+ routes
118
+ end
119
+
120
+ def get_channel_name_for(user_id)
121
+ channel_name = "some-unique-key-for-app-#{user_id}"
122
+ end
123
+
124
+ def dump(obj)
125
+ Yajl::Encoder.encode(obj)
126
+ end
127
+
128
+ def restore(str)
129
+ Yajl::Parser.parse(str)
130
+ end
131
+
132
+ def load_tasks
133
+ file_path = Pathname.new(__FILE__)
134
+ load File.join(file_path.dirname, "../../tasks/routes.rake")
135
+ end
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,23 @@
1
+ Webmate::Application.configure do |config|
2
+ config.app.load_paths = ["app/responders", "app/models", "app/services", "app/observers", "app/decorators"]
3
+ config.app.cache_classes = false
4
+
5
+ config.app.name = 'webmate'
6
+ config.app.host = 'localhost'
7
+ config.app.port = 80
8
+ config.app.host_with_port = Configatron::Delayed.new { "#{configatron.app.host}:#{configatron.app.port}" }
9
+
10
+ config.logger.path = "#{Webmate.root}/log"
11
+
12
+ config.assets.debug = false
13
+ config.assets.compress = false
14
+ config.assets.compile = true
15
+ config.assets.digest = false
16
+
17
+ config.cookies.key = Configatron::Delayed.new { "_#{configatron.app.name}_session" }
18
+ config.cookies.domain = nil
19
+ config.cookies.secret = "65e604cae451847ff2722ba84cb13db90f1b0a9ddc35a37169bec"
20
+
21
+ config.websockets.enabled = true
22
+ config.websockets.port = 80
23
+ end
@@ -0,0 +1,38 @@
1
+ module Webmate::Decorators
2
+ class Base
3
+ attr_reader :entity, :options
4
+
5
+ def initialize(entity, options = {})
6
+ @entity, @options = entity, options
7
+ end
8
+
9
+ class << self
10
+ def decorate(entity_or_collection, options = {})
11
+ if entity_or_collection.respond_to?(:map)
12
+ entity_or_collection.map { |e| self.decorate(e, options) }
13
+ else
14
+ self.new(entity_or_collection, options)
15
+ end
16
+ end
17
+ end
18
+
19
+ def id
20
+ entity.id.to_s
21
+ end
22
+
23
+ def method_missing(method, *args, &block)
24
+ if entity.respond_to?(method)
25
+ self.class.send :define_method, method do |*args, &blokk|
26
+ entity.send method, *args, &blokk
27
+ end
28
+
29
+ send method, *args, &block
30
+ else
31
+ super
32
+ end
33
+ rescue NoMethodError => no_method_error
34
+ super if no_method_error.name == method
35
+ raise no_method_error
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,17 @@
1
+ module Webmate
2
+ def self.root
3
+ WEBMATE_ROOT
4
+ end
5
+
6
+ def self.env
7
+ ENV["RACK_ENV"]
8
+ end
9
+
10
+ def self.env?(env)
11
+ self.env == env.to_s
12
+ end
13
+
14
+ def self.logger
15
+ @logger ||= Webmate::Logger.new
16
+ end
17
+ end
@@ -0,0 +1,20 @@
1
+ module Webmate
2
+ class Logger < Rack::CommonLogger
3
+ cattr_accessor :logger_file
4
+ def initialize(app = nil)
5
+ @app = app
6
+ Dir.mkdir(configatron.logger.path) unless File.exists?(configatron.logger.path)
7
+ @@logger_file ||= File.new(File.join(configatron.logger.path, "#{Webmate.env}.log"), 'a')
8
+ end
9
+
10
+ def log(env, status, header, began_at)
11
+ dump(%Q{HTTP #{env["REQUEST_METHOD"]}: #{env["PATH_INFO"]} #{status} \nParams: #{env['rack.request.query_hash']}})
12
+ end
13
+
14
+ def dump(text)
15
+ [@@logger_file, STDOUT].each do |out|
16
+ out.write %Q{[#{Time.now.strftime("%D %H:%M:%S")}] #{text} \n\n}
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,24 @@
1
+ module Webmate::Observers
2
+ class Base
3
+ class << self
4
+ cattr_accessor :subscriptions
5
+
6
+ def subscribe(action, &block)
7
+ Webmate::Observers::Base.subscribe!(action, &block)
8
+ end
9
+
10
+ def subscribe!(action, &block)
11
+ self.subscriptions ||= {}
12
+ self.subscriptions[action] ||= []
13
+ self.subscriptions[action] << block
14
+ end
15
+
16
+ def execute_all(action, data)
17
+ self.subscriptions ||= {}
18
+ (self.subscriptions[action] || []).each do |block|
19
+ block.call(data)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,69 @@
1
+ module Webmate::Presenters
2
+ class Base
3
+ attr_reader :attrs
4
+
5
+ def initialize(object, options = {})
6
+ @object, @options = object, options.with_indifferent_access
7
+ @attrs = {}
8
+ end
9
+
10
+ def namespace(name, &block)
11
+ serializer = self.class.new(@object, @options)
12
+ serializer.instance_exec(@object, &block)
13
+ self.attrs[name] = serializer.attrs
14
+ end
15
+
16
+ def resource(name, object = nil, &block)
17
+ raise "You should set name for resource" if name.blank?
18
+ raise "You should specify object" if @object.nil? && object.nil?
19
+ nested_name = name.to_s
20
+ nested_object = object || @object.send(nested_name)
21
+ if nested_object.blank?
22
+ self.attrs[nested_name] = {}
23
+ else
24
+ self.attrs[nested_name] = nested_resource(nested_name, nested_object, @options, &block)
25
+ end
26
+ end
27
+
28
+ def resources(name, objects = nil, &block)
29
+ raise "You should specify object" if @object.nil? && objects.nil?
30
+ objects = objects.flatten unless objects.nil?
31
+ nested_objects = objects || @object.send(name.to_s)
32
+ if nested_objects.blank?
33
+ self.attrs[name.to_s] = []
34
+ else
35
+ self.attrs[name.to_s] = (nested_objects || []).inject([]) do |result, obj|
36
+ resource = nested_resource(name, obj, @options, &block)
37
+ resource.empty? ? result : (result << resource)
38
+ end
39
+ end
40
+ end
41
+
42
+ def attributes(*attrs, &block)
43
+ if @object.blank?
44
+ object = attrs.last
45
+ attrs.delete(attrs.last)
46
+ raise ArgumentError, "Object was not specified" if object.is_a?(Symbol)
47
+ end
48
+
49
+ target = object || @object
50
+ Array.wrap(attrs).flatten.each do |attribute|
51
+ self.attrs[attribute.to_s] = target.send(attribute.to_s)
52
+ end
53
+ end
54
+
55
+ def attribute(attr, &block)
56
+ self.attrs[attr.to_s] = yield
57
+ end
58
+
59
+ protected
60
+
61
+ def nested_resource(name, object, options, &block)
62
+ return nil if !object || object.blank?
63
+ serializer = self.class.new(object, options)
64
+ serializer.instance_exec(object, &block)
65
+ serializer.attrs
66
+ end
67
+
68
+ end
69
+ end