I'm having a Rails 5 api only app using rspec and versioned this way :
app
- controllers
- api
- v1
- users_controller.rb
My api/v1/users_controller.rb :
module Api::V1
class UsersController < ApiController
My config\routes.rb :
Rails.application.routes.draw do
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
constraints subdomain: 'api' do
scope module: 'api' do
namespace :v1 do
resources :users
end
end
end
end
When I check the routes with rails routes it shows me.
Prefix Verb URI Pattern Controller#Action
v1_users GET /v1/users(.:format) api/v1/users#index {:subdomain=>"api"}
POST /v1/users(.:format) api/v1/users#create {:subdomain=>"api"}
v1_user GET /v1/users/:id(.:format) api/v1/users#show {:subdomain=>"api"}
PATCH /v1/users/:id(.:format) api/v1/users#update {:subdomain=>"api"}
PUT /v1/users/:id(.:format) api/v1/users#update {:subdomain=>"api"}
DELETE /v1/users/:id(.:format) api/v1/users#destroy {:subdomain=>"api"}
My spec file :
require "rails_helper"
RSpec.describe Api::V1::UsersController, type: :routing do
describe "routing" do
it "routes to #index" do
expect(:get => "/v1/users").to route_to("api/v1/users#index")
end
it "routes to #create" do
expect(:post => "/v1/users").to route_to("api/v1/users#create")
end
it "routes to #show" do
expect(:get => "/v1/users/1").to route_to("api/v1/users#show", :id => "1")
end
it "routes to #update via PUT" do
expect(:put => "/v1/users/1").to route_to("api/v1/users#update", :id => "1")
end
it "routes to #update via PATCH" do
expect(:patch => "/v1/users/1").to route_to("api/v1/users#update", :id => "1")
end
it "routes to #destroy" do
expect(:delete => "/v1/users/1").to route_to("api/v1/users#destroy", :id => "1")
end
end
end
But when I'm testing my routes with rspec it fails as it.
bundle exec rspec spec/routing/users_routing_spec.rb
FFFFF
Failures:
1) Api::V1::UsersController routing routes to #index
Failure/Error: expect(:get => "/v1/users").to route_to("api/v1/users#index")
No route matches "/v1/users"
# ./spec/routing/users_routing_spec.rb:7:in `block (3 levels) in <top (required)>'
I don't understand why. Any idea ?
You have to specify "subdomain" for your spec.
before do
Rails.application.routes.default_url_options[:host] = 'test.host'
end
it "routes to #index" do
expect(:get => v1_users_url).to route_to('v1/users#index', subdomain: 'api')
end
Related
Current Detail
[Env]
nginx puma mysql
Rails == 5.1.5
I mounted carrierwave to upload pictures and files in a post.
[Case]
Users not logged in type the file URL in browser, then they can access and view the file.
[Ideal]
Only users logged in is accessible to the file.
The file path is "uploads/post/images/1234(post_id)/sample.png".
So far, I locate uploads directory under public, app/assets/, and root directory in vain.
Any answers or suggestions are appreciated.
Source
Rails.application.routes.draw do
get 'users/index'
get 'users/show'
get 'posts/index'
devise_for :users, module: :users
resources :users, :only => [:index, :show]
get "/" => "posts#index"
get "posts/like_ranking" => "posts#like_rank"
get "posts/post_count_ranking" => "posts#post_count"
get "posts/tags_search" => "posts#tags_search"
get "posts/new" => "posts#new"
get "posts/:id/reply" => "posts#new"
post "posts/create" => "posts#create"
get "posts/:id" => "posts#show"
get "posts/:id/edit" => "posts#edit"
post "posts/:id/update" => "posts#update"
post "posts/:id/destroy" => "posts#destroy"
get 'tags/:tag', to: 'posts#index', as: :tag
get "users/:id/likes" => "users#likes"
get "users/:id/reply" => "users#reply"
resources :posts, only: %w(index)
resources :posts, shallow: true do
resources :likes, only: [:create, :destroy]
end
end
"
class ImageUploader < CarrierWave::Uploader::Base
# Choose what kind of storage to use for this uploader:
storage :file
# Override the directory where uploaded files will be stored.
# This is a sensible default for uploaders that are meant to be mounted:
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
end
All assets under the public folder is served by nginx. In order to make it available for logged users you can first upload files to other directory than public and create action which serve files by Rails app from that directory and check if users logged before serving.
I got a problem in generate a json file using a template inside a chef template.
I need to generate a json file like below, called plugin.json:
{
"agents": [
{
"name" : "sqlhost",
"host" : "localhost",
"metrics" : "status,newrelic",
"user" : "root",
"passwd" : "mysecret"
}
]
}
This file will be used by the Newrelic plugin
Template File plugin.json.erb:
<%= Chef::JSONCompat.to_json_pretty(#name) %>
I've created a recipe like this one:
mysql-plugin.rb
# Default Variables
newrelic = decrypt_databag('newrelic')
node.default['newrelic_npi']['license_key'] = newrelic['license_key']
plugin = 'com.newrelic.plugins.mysql.instance'
plugin_json_path = "/opt/newrelic-npi/plugins/com.newrelic.plugins.mysql.instance/newrelic_mysql_plugin-2.0.0/config/plugin.json"
node.default['newrelic_npi']['name'] = "localhost1"
execute 'fetch' do
cwd node['newrelic_npi']['install_path']
user node['newrelic_npi']['user']
command "./npi fetch #{plugin} -y"
not_if do
::File.exist? "#{node['newrelic_npi']['install_path']}/plugins/#{plugin}"
end
end
execute 'prepare' do
cwd node['newrelic_npi']['install_path']
user node['newrelic_npi']['user']
command "./npi prepare #{plugin}"
end
template "#{plugin_json_path}" do
source 'plugin.json.erb'
owner "root"
group "root"
mode "0644"
variables :name => node['newrelic_npi']['name']
action :create
end
execute 'add-service' do
cwd node['newrelic_npi']['install_path']
user node['newrelic_npi']['user']
command "sudo ./npi add-service #{plugin} --start" # needs root privileges
not_if do
::File.exist? "/etc/init.d/newrelic_plugin_#{plugin}"
end
end
At the Template block what should I do to make a Json file like plugin.json?
template "#{plugin_json_path}" do
source 'plugin.json.erb'
owner "root"
group "root"
mode "0644"
variables :name => node['newrelic_npi']['name']
action :create
end
Thanks in Advance.
First. You should be setting any default node variables inside default.rb in your attributes folder. Like this:
default['newrelic_npi']['license_key'] = newrelic['license_key']
default['newrelic_npi']['name'] = "localhost1"
Node attributes are reset before compilation and will pull from that file as well as Ohai. Setting node attributes inside the recipe means they aren't going to be available for your template (you should review this Chef Client Overview).
Next, if you just need to set the hostname or another specific portion of the template file you can do this inside your erb file:
{
"agents": [
{
"name" : "sqlhost",
"host" : "<%= #name %>",
"metrics" : "status,newrelic",
"user" : "root",
"passwd" : "mysecret"
}
]
}
Your current erb file is trying to convert #name (which you try to set to localhost1) to json, not set the whole file like your example.
To generate a correct plugin.json:
{
"agents": [
{
"name" : <%= Chef::JSONCompat.to_json_pretty(#name) %>,
"host" : <%= Chef::JSONCompat.to_json_pretty(#host) %>,
"metrics" : <%= Chef::JSONCompat.to_json_pretty(#metrics) %>,
"user" : <%= Chef::JSONCompat.to_json_pretty(#user) %>,
"passwd" : <%= Chef::JSONCompat.to_json_pretty(#passwd) %>
}
]
}
At the template Block I did like:
template node['newrelic_npi']['plugin_json_path'] do
source 'plugin.json.erb'
owner 'root'
group 'root'
mode '0644'
variables(
:name => node['newrelic_npi']['name'],
:host => node['newrelic_npi']['host'],
:metrics => node['newrelic_npi']['metrics'],
:user => node['newrelic_npi']['user'],
:passwd => node['newrelic_npi']['myql_admin_password']
)
action :create
end
Hi Im trying to set up a database to store emails using ruby, sinatra, ActiveRecord, and mysql. Any suggestions on what im doing wrong? Im trying to output it to a seperate page that only i can see and then post it using a url on hostgator.
require 'sinatra'
require 'activerecord'
# require 'sinatra-activerecord'
get '/' do
erb :index
end
def save_email (email)
file.print(email)
end
get '/email' do
params[:email]
# # redirect '/'
end
post '/email' do
params[:email]
#email = params[:email]
erb :email, :locals => {:email => params[:email]}
end
# Change the following to reflect your database settings
ActiveRecord::Base.establish_connection(
adapter: 'mysql', # or 'postgresql'
host: 'localhost',
database: 'Stored_Emails',
)
class Stored_Emails < Sinatra::Application
end
class Stored_Emails < ActiveRecord::Base
end
ActiveRecord::Migration.create_table :email do |t|
t.string :emails
end
create_table :emails, force: true do |t|
t.string :email
t.belongs_to :email, index: true
end
get '/email' do
params[:email].all
end
Typically you break out your code into multiple files (we use folders named config, helpers, libraries, views, routes, models, migrations) and require them at the top of your app. However, if you want to put it in the same file and just use that and a Gemfile and Gemfile.lock that'll work too. Here's how it might look:
# Require your gems
require 'sinatra'
require 'activerecord'
# Libraries
# Models
class Stored_Emails < ActiveRecord::Base
end
# Configuration
# Change the following to reflect your database settings
ActiveRecord::Base.establish_connection(
adapter: 'mysql', # or 'postgresql'
host: 'localhost',
database: 'Stored_Emails'
)
ActiveRecord::Migration.create_table :email do |t|
t.string :emails
end
# Migrations
create_table :emails, force: true do |t|
t.string :email
end
# Helpers
def save_email (email)
file.print(email)
end
# Routes
get '/' do
# Load whatever you want to show in your index page into class variables
erb :index
end
get '/email' do
Stored_Emails.all.to_json
end
post '/email' do
#email = Stored_Emails.find_by(params[:email])
erb :email
end
Now you're going to have to do a good bit of work to get this running. Here's what I suggest you read:
1) Sinatra documentation - http://www.sinatrarb.com/intro.html
routes
running your sinatra app
views with ERB
2) Bundler documentation for gems - http://bundler.io/
3) ActiveRecord documentation - http://guides.rubyonrails.org/active_record_basics.html
Connecting to a database Creating your database with a migration -
this is a one time deal Querying the database
Good Luck!
I'm working on a Rails 5 API application and Im having trouble when trying to render json. Take that simple controller:
module Api
module V1
class UsersController < ApplicationController
def sign_up
#user = User.new user_params
if #user.save
render json: {status: :ok, user: #user.to_json }
else
render json: { status: :error, errors: #user.errors }, status: :unprocessable_entity
end
end
end
end
end
When I test this request with a simple spec:
RSpec.describe Api::V1::UsersController, type: :controller do
context 'As an unregistered user' do
context 'signing up with valid params' do
let(:valid_params) do
{ name: 'Test User', email: 'test#test.com', password: '123456789', password_confirmation: '123456789' }
end
it 'returns a 200 status' do
post :sign_up, params: { user: valid_params }
expect(response.status).to eq 200
end
end
end
end
I get an error thrown at me:
Failures:
1) Api::V1::UsersController As an unregistered user signing up with valid status returns a 200 status
Failure/Error: render json: {status: :ok, user: #user.to_json }
NoMethodError:
undefined method `unpack' for nil:NilClass
# ./app/controllers/api/v1/users_controller.rb:10:in `sign_up'
# ./spec/controllers/api/v1/users_controller_spec.rb:15:in `block (4 levels) in <top (required)>'
I'm not using any gems that alter rendering yet so I'm really lost of what might be the problem. Thanks in advance.
My Rails 3.1 application connects to 2 databases, one is the default, the other is an Amazon RDS MYSQL instance.
The current database.yml contains two production database connections. The models that need to pull from the second database simply use
establish_connection "production_on_amazon"
Unfortunately Heroku overwrites your database.yml, and only seems to inlcude one database connection. Does anyone know how I can add or configure my second?
Running "heroku config" I can see there are 2 DB's listed but cant seem to configure to connect to both. Perhaps somehow set my default to the SHARED_DATABASE_URL db on Heroku and set the alternate to the DATABASE_URL which points to Amazon...
Working off of the previous responses, but incorporating some Rails 3 advantages with the configuration and simplifying the parsing...
# config/application.rb
module MyApp
class Application < Rails::Application
... other configs
config.secondary_database_url = ENV['SECONDARY_DB_URL']
end
end
We may want to override this in development / test
# config/environments/development.rb
module MyApp
class Application < Rails::Application
... other configs
config.secondary_database_url = 'SOME_CONNECTION_STRING'
end
end
Now to setup the class we'll have our models inherit from...
# lib/active_record/secondary.rb
module ActiveRecord
class Secondary < ActiveRecord::Base
self.abstract_class = true
# prior to AR 3.2.1
url = URI.parse( MyApp::Application.config.secondary_database_url )
establish_connection(
:adapter => 'mysql',
:host => url.host,
:username => url.userinfo.split(':')[0],
:password => url.userinfo.split(':')[1],
:database => url.path[1..-1],
:port => url.port || 3306
)
# as of AR 3.2.1
establish_connection(MyApp::Application.config.secondary_database_url)
end
class SecondaryMigration < ActiveRecord::Migration
def connection
ActiveRecord::Secondary.connection
end
end
end
Heroku will always connect your app to the production DB that they create for you. If you want to make an additional connection you'll need to do this in your code manually, and create a ENV var that the code can use as a connection string.
Anything in the production segment of database.yml is binned by Heroku and replaced.
Regarding Neil's answer, here is a way to do it. Not an out-of-box solution, but might give you an idea...
/lib/active_record_extensions.rb
module ActiveRecordExtensions
class Shard < ActiveRecord::Base
#need to switch to the shard database connection from heroku config
primary_database_url = ENV['PRIMARY_DATABASE_URL']
if(!primary_database_url.nil?)
parsed_connection_string = primary_database_url.split("://")
adapter = parsed_connection_string[0]
parsed_connection_string = parsed_connection_string[1].split(":")
username = parsed_connection_string[0]
parsed_connection_string = parsed_connection_string[1].split("#")
password = parsed_connection_string[0]
parsed_connection_string = parsed_connection_string[1].split("/")
host = parsed_connection_string[0]
database = parsed_connection_string[1]
establish_connection(
:adapter => adapter,
:host => host,
:username => username,
:password => password,
:database => database,
:port => 3306,
:pool => 5,
:timeout => 5000
)
else
self.establish_connection "shard_#{Rails.env}"
end
end
class ShardMigration < ActiveRecord::Migration
def connection
ActiveRecord::Shard.connection
end
end
end
So your model should just extend ActiveRecord::Shard instead of Base