Implemented RuboCop, Ruby linter.

This commit is contained in:
dB 2015-01-13 18:50:18 -05:00
parent cc6389f0ca
commit 04c21470e3
16 changed files with 125 additions and 107 deletions

2
.rubocop.yml Normal file
View file

@ -0,0 +1,2 @@
inherit_from: .rubocop_todo.yml

24
.rubocop_todo.yml Normal file
View file

@ -0,0 +1,24 @@
# This configuration was generated by `rubocop --auto-gen-config`
# on 2015-01-13 18:47:14 -0500 using RuboCop version 0.28.0.
# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
# versions of RuboCop, may require this file to be generated again.
# Offense count: 25
# Configuration parameters: AllowURI, URISchemes.
Metrics/LineLength:
Max: 179
# Offense count: 7
Style/Documentation:
Enabled: false
# Offense count: 2
# Configuration parameters: Exclude.
Style/FileName:
Enabled: false
# Offense count: 4
Style/RegexpLiteral:
MaxSlashes: 0

View file

@ -4,15 +4,15 @@
guard 'rspec' do guard 'rspec' do
watch(%r{^spec/.+_spec\.rb$}) watch(%r{^spec/.+_spec\.rb$})
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" } watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
watch('spec/spec_helper.rb') { "spec" } watch('spec/spec_helper.rb') { 'spec' }
# Rails example # Rails example
watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" } watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
watch(%r{^app/(.*)(\.erb|\.haml)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" } watch(%r{^app/(.*)(\.erb|\.haml)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] } watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
watch(%r{^spec/support/(.+)\.rb$}) { "spec" } watch(%r{^spec/support/(.+)\.rb$}) { 'spec' }
watch('config/routes.rb') { "spec/routing" } watch('config/routes.rb') { 'spec/routing' }
watch('app/controllers/application_controller.rb') { "spec/controllers" } watch('app/controllers/application_controller.rb') { 'spec/controllers' }
# Capybara features specs # Capybara features specs
watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/features/#{m[1]}_spec.rb" } watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/features/#{m[1]}_spec.rb" }
@ -21,4 +21,3 @@ guard 'rspec' do
watch(%r{^spec/acceptance/(.+)\.feature$}) watch(%r{^spec/acceptance/(.+)\.feature$})
watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' } watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' }
end end

View file

@ -82,18 +82,18 @@ end
```ruby ```ruby
# Serializer options can be specified on routes or namespaces. # Serializer options can be specified on routes or namespaces.
namespace 'foo', :serializer => BarSerializer do namespace 'foo', serializer: BarSerializer do
get "/" do get "/" do
# will use "bar" serializer # will use "bar" serializer
end end
# Options specified on a route or namespace override those of the containing namespace. # Options specified on a route or namespace override those of the containing namespace.
get "/home", :serializer => HomeSerializer do get "/home", serializer: HomeSerializer do
# will use "home" serializer # will use "home" serializer
end end
# All standard options for `ActiveModel::Serializers` are supported. # All standard options for `ActiveModel::Serializers` are supported.
get "/fancy_homes", :root => 'world', :each_serializer => FancyHomesSerializer get "/fancy_homes", root: 'world', each_serializer: FancyHomesSerializer
... ...
end end
end end
@ -130,7 +130,7 @@ helper method:
```ruby ```ruby
helpers do helpers do
def current_user def current_user
@current_user ||= User.where( :access_token => params[:token]).first @current_user ||= User.where(access_token: params[:token]).first
end end
def authenticate! def authenticate!
@ -168,7 +168,7 @@ class API < Grape::API
end end
end end
API.new.get "/home" # => '{:user=>{:first_name=>"JR", :last_name=>"HE"}}' API.new.get "/home" # => '{ user: { first_name: "JR", last_name: "HE" } }'
``` ```

View file

@ -1,5 +1,5 @@
#!/usr/bin/env rake #!/usr/bin/env rake
require "bundler/gem_tasks" require 'bundler/gem_tasks'
require 'rspec/core' require 'rspec/core'
require 'rspec/core/rake_task' require 'rspec/core/rake_task'
@ -8,4 +8,7 @@ RSpec::Core::RakeTask.new(:spec) do |spec|
spec.pattern = FileList['spec/**/*_spec.rb'] spec.pattern = FileList['spec/**/*_spec.rb']
end end
task :default => :spec require 'rubocop/rake_task'
RuboCop::RakeTask.new(:rubocop)
task default: [:rubocop, :spec]

View file

@ -2,25 +2,26 @@
require File.expand_path('../lib/grape-active_model_serializers/version', __FILE__) require File.expand_path('../lib/grape-active_model_serializers/version', __FILE__)
Gem::Specification.new do |gem| Gem::Specification.new do |gem|
gem.authors = ["Jonathan Richard Henry Evans"] gem.authors = ['Jonathan Richard Henry Evans']
gem.email = ["contact@jrhe.co.uk"] gem.email = ['contact@jrhe.co.uk']
gem.summary = %q{Use active_model_serializer in grape} gem.summary = 'Use active_model_serializer in grape'
gem.description = %q{Provides a Formatter for the Grape API DSL to emit objects serialized with active_model_serializers.} gem.description = 'Provides a Formatter for the Grape API DSL to emit objects serialized with active_model_serializers.'
gem.homepage = "https://github.com/jrhe/grape-active_model_serializers" gem.homepage = 'https://github.com/jrhe/grape-active_model_serializers'
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } gem.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
gem.files = `git ls-files`.split("\n") gem.files = `git ls-files`.split("\n")
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
gem.name = "grape-active_model_serializers" gem.name = 'grape-active_model_serializers'
gem.require_paths = ["lib"] gem.require_paths = ['lib']
gem.version = Grape::ActiveModelSerializers::VERSION gem.version = Grape::ActiveModelSerializers::VERSION
gem.licenses = ['MIT'] gem.licenses = ['MIT']
gem.add_dependency "grape" gem.add_dependency 'grape'
gem.add_dependency "active_model_serializers", ">= 0.9.0" gem.add_dependency 'active_model_serializers', '>= 0.9.0'
gem.add_development_dependency "rspec" gem.add_development_dependency 'rspec'
gem.add_development_dependency "rack-test" gem.add_development_dependency 'rack-test'
gem.add_development_dependency "rake" gem.add_development_dependency 'rake'
gem.add_development_dependency "guard-rspec" gem.add_development_dependency 'guard-rspec'
gem.add_development_dependency 'rubocop', '0.28.0'
end end

View file

@ -35,22 +35,18 @@ module Grape
end end
end end
def render(resources, meta={}) def render(resources, meta = {})
set_meta_and_meta_key(meta) Formatter::ActiveModelSerializers.meta = meta[:meta]
Formatter::ActiveModelSerializers.meta_key = meta[:meta_key]
resources resources
end end
def default_serializer_options; end def default_serializer_options
def url_options; end
private
def set_meta_and_meta_key(meta)
Formatter::ActiveModelSerializers.meta = meta[:meta]
Formatter::ActiveModelSerializers.meta_key = meta[:meta_key]
end end
def url_options
end
end end
Endpoint.send(:include, EndpointExtension) Endpoint.send(:include, EndpointExtension)
end end

View file

@ -16,12 +16,13 @@ module Grape
endpoint = env['api.endpoint'] endpoint = env['api.endpoint']
options = build_options_from_endpoint(endpoint) options = build_options_from_endpoint(endpoint)
if serializer = options.fetch(:serializer, ActiveModel::Serializer.serializer_for(resource)) serializer = options.fetch(:serializer, ActiveModel::Serializer.serializer_for(resource))
options[:scope] = endpoint unless options.has_key?(:scope) return nil unless serializer
# ensure we have an root to fallback on
options[:resource_name] = default_root(endpoint) if resource.respond_to?(:to_ary) options[:scope] = endpoint unless options.key?(:scope)
serializer.new(resource, options.merge(other_options)) # ensure we have an root to fallback on
end options[:resource_name] = default_root(endpoint) if resource.respond_to?(:to_ary)
serializer.new(resource, options.merge(other_options))
end end
def other_options def other_options
@ -57,10 +58,10 @@ module Grape
# otherwise the route name (e.g. get 'name') # otherwise the route name (e.g. get 'name')
def default_root(endpoint) def default_root(endpoint)
innermost_scope = if endpoint.respond_to?(:namespace_stackable) innermost_scope = if endpoint.respond_to?(:namespace_stackable)
endpoint.namespace_stackable(:namespace).last endpoint.namespace_stackable(:namespace).last
else else
endpoint.settings.peek[:namespace] endpoint.settings.peek[:namespace]
end end
if innermost_scope if innermost_scope
innermost_scope.space innermost_scope.space

View file

@ -20,43 +20,40 @@ describe '#render' do
context 'with meta key' do context 'with meta key' do
it 'includes meta key and content' do it 'includes meta key and content' do
result = get_resource_with({ meta: { total: 2 }}) result = get_resource_with(meta: { total: 2 })
expect(result).to have_key('meta') expect(result).to have_key('meta')
expect(result.fetch('meta')).to eq({ 'total' => 2 }) expect(result.fetch('meta')).to eq('total' => 2)
end end
end end
context 'with a custom meta_key' do context 'with a custom meta_key' do
it 'includes the custom meta key name' do it 'includes the custom meta key name' do
result = get_resource_with({ meta: { total: 2 }, meta_key: :custom_key_name }) result = get_resource_with(meta: { total: 2 }, meta_key: :custom_key_name)
expect(result).to have_key('custom_key_name') expect(result).to have_key('custom_key_name')
expect(result.fetch('custom_key_name')).to eq({ 'total' => 2 }) expect(result.fetch('custom_key_name')).to eq('total' => 2)
end end
it 'ignores a lonely meta_key' do it 'ignores a lonely meta_key' do
result = get_resource_with({ meta_key: :custom_key_name }) result = get_resource_with(meta_key: :custom_key_name)
expect(result).not_to have_key('meta') expect(result).not_to have_key('meta')
expect(result).not_to have_key('custom_key_name') expect(result).not_to have_key('custom_key_name')
end end
end end
context 'junk keys' do context 'junk keys' do
it 'ignores junk keys' do it 'ignores junk keys' do
result = get_resource_with({ junk_key: { total: 2 } }) result = get_resource_with(junk_key: { total: 2 })
expect(result).not_to have_key('junk_key') expect(result).not_to have_key('junk_key')
end end
it 'ignores empty meta_key' do it 'ignores empty meta_key' do
result = get_resource_with({ meta: { total: 2 }, meta_key: nil }) result = get_resource_with(meta: { total: 2 }, meta_key: nil)
expect(result).to have_key('meta') expect(result).to have_key('meta')
end end
it 'ignores empty meta' do it 'ignores empty meta' do
result = get_resource_with({ meta: nil }) result = get_resource_with(meta: nil)
expect(result).not_to have_key('meta') expect(result).not_to have_key('meta')
end end
end end
end end

View file

@ -1,11 +1,10 @@
require 'spec_helper' require 'spec_helper'
describe 'Grape::EndpointExtension' do describe 'Grape::EndpointExtension' do
if Grape::Util.const_defined?('InheritableSetting') if Grape::Util.const_defined?('InheritableSetting')
subject { Grape::Endpoint.new(Grape::Util::InheritableSetting.new, {path: '/', method: 'foo'}) } subject { Grape::Endpoint.new(Grape::Util::InheritableSetting.new, path: '/', method: 'foo') }
else else
subject { Grape::Endpoint.new({}, {path: '/', method: 'foo'}) } subject { Grape::Endpoint.new({}, path: '/', method: 'foo') }
end end
let(:serializer) { Grape::Formatter::ActiveModelSerializers } let(:serializer) { Grape::Formatter::ActiveModelSerializers }
@ -20,10 +19,10 @@ describe 'Grape::EndpointExtension' do
let(:users) { [user, user] } let(:users) { [user, user] }
describe "#render" do describe '#render' do
it { should respond_to(:render) } it { should respond_to(:render) }
let (:meta_content) { { total: 2 } } let(:meta_content) { { total: 2 } }
let (:meta_full) { { meta: meta_content } } let(:meta_full) { { meta: meta_content } }
context 'supplying meta' do context 'supplying meta' do
it 'passes through the Resource and uses given meta settings' do it 'passes through the Resource and uses given meta settings' do
expect(serializer).to receive(:meta=).with(meta_content) expect(serializer).to receive(:meta=).with(meta_content)
@ -31,7 +30,7 @@ describe 'Grape::EndpointExtension' do
end end
end end
context 'supplying meta and key' do context 'supplying meta and key' do
let (:meta_key) { { meta_key: :custom_key_name } } let(:meta_key) { { meta_key: :custom_key_name } }
it 'passes through the Resource and uses given meta settings' do it 'passes through the Resource and uses given meta settings' do
expect(serializer).to receive(:meta=).with(meta_content) expect(serializer).to receive(:meta=).with(meta_content)
expect(serializer).to receive(:meta_key=).with(meta_key[:meta_key]) expect(serializer).to receive(:meta_key=).with(meta_key[:meta_key])
@ -39,5 +38,4 @@ describe 'Grape::EndpointExtension' do
end end
end end
end end
end end

View file

@ -16,7 +16,7 @@ describe Grape::Formatter::ActiveModelSerializers do
it 'will wrap valid input in the meta: {} wrapper' do it 'will wrap valid input in the meta: {} wrapper' do
subject.meta = { total: 2 } subject.meta = { total: 2 }
expect(subject.meta).to eq({ meta: { total: 2 } }) expect(subject.meta).to eq(meta: { total: 2 })
end end
end end
@ -28,7 +28,7 @@ describe Grape::Formatter::ActiveModelSerializers do
it 'will wrap valid input in the meta_key: {} wrapper' do it 'will wrap valid input in the meta_key: {} wrapper' do
subject.meta_key = :custom_key_name subject.meta_key = :custom_key_name
expect(subject.meta_key).to eq({ meta_key: :custom_key_name }) expect(subject.meta_key).to eq(meta_key: :custom_key_name)
end end
end end
@ -36,9 +36,9 @@ describe Grape::Formatter::ActiveModelSerializers do
let(:user) { User.new(first_name: 'John') } let(:user) { User.new(first_name: 'John') }
if Grape::Util.const_defined?('InheritableSetting') if Grape::Util.const_defined?('InheritableSetting')
let(:endpoint) { Grape::Endpoint.new(Grape::Util::InheritableSetting.new, {path: '/', method: 'foo'}) } let(:endpoint) { Grape::Endpoint.new(Grape::Util::InheritableSetting.new, path: '/', method: 'foo') }
else else
let(:endpoint) { Grape::Endpoint.new({}, {path: '/', method: 'foo'}) } let(:endpoint) { Grape::Endpoint.new({}, path: '/', method: 'foo') }
end end
let(:env) { { 'api.endpoint' => endpoint } } let(:env) { { 'api.endpoint' => endpoint } }

View file

@ -1,5 +1,4 @@
require 'spec_helper' require 'spec_helper'
describe 'grape-active_model_serializers' do describe 'grape-active_model_serializers' do
end end

View file

@ -3,7 +3,7 @@ require 'support/models/user'
require 'support/models/blog_post' require 'support/models/blog_post'
require 'support/serializers/user_serializer' require 'support/serializers/user_serializer'
require 'support/serializers/blog_post_serializer' require 'support/serializers/blog_post_serializer'
require "grape-active_model_serializers" require 'grape-active_model_serializers'
describe Grape::ActiveModelSerializers do describe Grape::ActiveModelSerializers do
let(:app) { Class.new(Grape::API) } let(:app) { Class.new(Grape::API) }
@ -14,79 +14,78 @@ describe Grape::ActiveModelSerializers do
app.formatter :json, Grape::Formatter::ActiveModelSerializers app.formatter :json, Grape::Formatter::ActiveModelSerializers
end end
it 'should respond with proper content-type' do
it "should respond with proper content-type" do app.get('/home/users', serializer: UserSerializer) do
app.get("/home/users", :serializer => UserSerializer) do
User.new User.new
end end
get("/home/users") get('/home/users')
expect(last_response.headers["Content-Type"]).to eql "application/json" expect(last_response.headers['Content-Type']).to eql 'application/json'
end end
context 'serializer is set to nil' do context 'serializer is set to nil' do
before do before do
app.get("/home", serializer: nil) do app.get('/home', serializer: nil) do
{user: {first_name: "JR", last_name: "HE"}} { user: { first_name: 'JR', last_name: 'HE' } }
end end
end end
it 'uses the built in grape serializer' do it 'uses the built in grape serializer' do
get("/home") get('/home')
expect(subject).to eql "{\"user\":{\"first_name\":\"JR\",\"last_name\":\"HE\"}}" expect(subject).to eql "{\"user\":{\"first_name\":\"JR\",\"last_name\":\"HE\"}}"
end end
end end
context "serializer isn't set" do context "serializer isn't set" do
before do before do
app.get("/home") do app.get('/home') do
User.new({first_name: 'JR', last_name: 'HE', email: 'contact@jrhe.co.uk'}) User.new(first_name: 'JR', last_name: 'HE', email: 'contact@jrhe.co.uk')
end end
end end
it 'infers the serializer' do it 'infers the serializer' do
get "/home" get '/home'
expect(subject).to eql "{\"user\":{\"first_name\":\"JR\",\"last_name\":\"HE\"}}" expect(subject).to eql "{\"user\":{\"first_name\":\"JR\",\"last_name\":\"HE\"}}"
end end
end end
it "serializes arrays of objects" do it 'serializes arrays of objects' do
app.get("/users") do app.get('/users') do
user = User.new({first_name: 'JR', last_name: 'HE', email: 'contact@jrhe.co.uk'}) user = User.new(first_name: 'JR', last_name: 'HE', email: 'contact@jrhe.co.uk')
[user, user] [user, user]
end end
get "/users" get '/users'
expect(subject).to eql "{\"users\":[{\"first_name\":\"JR\",\"last_name\":\"HE\"},{\"first_name\":\"JR\",\"last_name\":\"HE\"}]}" expect(subject).to eql "{\"users\":[{\"first_name\":\"JR\",\"last_name\":\"HE\"},{\"first_name\":\"JR\",\"last_name\":\"HE\"}]}"
end end
context "models with compound names" do context 'models with compound names' do
it "generates the proper 'root' node for individual objects" do it "generates the proper 'root' node for individual objects" do
app.get("/home") do app.get('/home') do
BlogPost.new({title: 'Grape AM::S Rocks!', body: 'Really, it does.'}) BlogPost.new(title: 'Grape AM::S Rocks!', body: 'Really, it does.')
end end
get "/home" get '/home'
expect(subject).to eql "{\"blog_post\":{\"title\":\"Grape AM::S Rocks!\",\"body\":\"Really, it does.\"}}" expect(subject).to eql "{\"blog_post\":{\"title\":\"Grape AM::S Rocks!\",\"body\":\"Really, it does.\"}}"
end end
it "generates the proper 'root' node for serialized arrays" do it "generates the proper 'root' node for serialized arrays" do
app.get("/blog_posts") do app.get('/blog_posts') do
blog_post = BlogPost.new({title: 'Grape AM::S Rocks!', body: 'Really, it does.'}) blog_post = BlogPost.new(title: 'Grape AM::S Rocks!', body: 'Really, it does.')
[blog_post, blog_post] [blog_post, blog_post]
end end
get "/blog_posts" get '/blog_posts'
expect(subject).to eql "{\"blog_posts\":[{\"title\":\"Grape AM::S Rocks!\",\"body\":\"Really, it does.\"},{\"title\":\"Grape AM::S Rocks!\",\"body\":\"Really, it does.\"}]}" expect(subject).to eql "{\"blog_posts\":[{\"title\":\"Grape AM::S Rocks!\",\"body\":\"Really, it does.\"},{\"title\":\"Grape AM::S Rocks!\",\"body\":\"Really, it does.\"}]}"
end end
end end
it "uses namespace options when provided" do it 'uses namespace options when provided' do
app.namespace :admin, :serializer => UserSerializer do app.namespace :admin, serializer: UserSerializer do
get('/jeff') do get('/jeff') do
User.new(first_name: 'Jeff') User.new(first_name: 'Jeff')
end end
end end
get "/admin/jeff" get '/admin/jeff'
expect(subject).to eql "{\"user\":{\"first_name\":\"Jeff\",\"last_name\":null}}" expect(subject).to eql "{\"user\":{\"first_name\":\"Jeff\",\"last_name\":null}}"
end end
@ -99,7 +98,7 @@ describe Grape::ActiveModelSerializers do
end end
end end
get "/admin/jeff" get '/admin/jeff'
expect(subject).to eql "{\"admin\":[{\"first_name\":\"Jeff\",\"last_name\":null},{\"first_name\":\"Jeff\",\"last_name\":null}]}" expect(subject).to eql "{\"admin\":[{\"first_name\":\"Jeff\",\"last_name\":null},{\"first_name\":\"Jeff\",\"last_name\":null}]}"
end end
end end
@ -111,9 +110,8 @@ describe Grape::ActiveModelSerializers do
[user, user] [user, user]
end end
get "/people" get '/people'
expect(subject).to eql "{\"people\":[{\"first_name\":\"Jeff\",\"last_name\":null},{\"first_name\":\"Jeff\",\"last_name\":null}]}" expect(subject).to eql "{\"people\":[{\"first_name\":\"Jeff\",\"last_name\":null},{\"first_name\":\"Jeff\",\"last_name\":null}]}"
end end
end end
end end

View file

@ -4,9 +4,9 @@ $LOAD_PATH.unshift(File.dirname(__FILE__))
require 'bundler' require 'bundler'
Bundler.setup :default, :test Bundler.setup :default, :test
require "active_model_serializers" require 'active_model_serializers'
require 'active_support/core_ext/hash/conversions' require 'active_support/core_ext/hash/conversions'
require "active_support/json" require 'active_support/json'
require 'rspec' require 'rspec'
require 'rack/test' require 'rack/test'
require 'grape-active_model_serializers' require 'grape-active_model_serializers'
@ -15,4 +15,4 @@ RSpec.configure do |config|
config.include Rack::Test::Methods config.include Rack::Test::Methods
end end
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f} Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }

View file

@ -2,8 +2,8 @@ class BlogPost
include ActiveModel::SerializerSupport include ActiveModel::SerializerSupport
attr_accessor :title, :body attr_accessor :title, :body
def initialize(params={}) def initialize(params = {})
params.each do |k,v| params.each do |k, v|
instance_variable_set("@#{k}", v) unless v.nil? instance_variable_set("@#{k}", v) unless v.nil?
end end
end end

View file

@ -2,8 +2,8 @@ class User
include ActiveModel::SerializerSupport include ActiveModel::SerializerSupport
attr_accessor :first_name, :last_name, :password, :email attr_accessor :first_name, :last_name, :password, :email
def initialize(params={}) def initialize(params = {})
params.each do |k,v| params.each do |k, v|
instance_variable_set("@#{k}", v) unless v.nil? instance_variable_set("@#{k}", v) unless v.nil?
end end
end end