草庐IT

android - 依靠聊天无法正常工作 QuickBlox

coder 2023-12-05 原文

我正在为我的 android 应用程序使用 quickblox。我能够注册、登录用户,甚至添加自定义对象并从 quick-blox 服务器检索它们,但在聊天列表屏幕中我的计数出错了。

我遇到未读计数问题

这是我的代码流程:-

public class ConversationFragment extends Fragment implements Observer, DialogsManager.ManagingDialogsCallbacks {
    ConversationViewModel conversationViewModel;
    FragmentConversationBinding binding;
    QBChatDialogMessageListener allDialogsMessagesListener;
    SystemMessagesListener systemMessagesListener;
    QBSystemMessagesManager systemMessagesManager;
    QBIncomingMessagesManager incomingMessagesManager;
    private DialogsManager dialogsManager;

    public ConversationFragment() {
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        binding = DataBindingUtil.inflate(inflater, R.layout.fragment_conversation, container, false);
        getActivity().getWindow().setBackgroundDrawableResource(R.drawable.bg_img);
        View view = binding.getRoot();
        systemMessagesListener = new SystemMessagesListener();
        dialogsManager = new DialogsManager();

        return view;
    }

    private void setUpModel() {
        Bundle mBundle = getArguments();
        if (mBundle != null) {
            conversationViewModel = new ConversationViewModel(getActivity(), binding, getArguments().getString("DialogIdData"));
        } else {
            conversationViewModel = new ConversationViewModel(getActivity(), binding, "");
        }

        binding.setConversationViewModel(conversationViewModel);
        setUpObserver(conversationViewModel);

    }


    private void setUpObserver(ConversationViewModel observer) {
        observer.addObserver(this);
    }

    @Override
    public void update(Observable o, Object arg) {
    }

    @Override
    public void onDestroy() {
        super.onDestroy();

    }

    @Override
    public void onResume() {
        super.onResume();
        setUpModel();

    }

    @Override
    public void onPause() {
        super.onPause();
        unregisterQbChatListeners();
    }


    public void unregisterQbChatListeners() {
        if (incomingMessagesManager != null) {
            incomingMessagesManager.removeDialogMessageListrener(allDialogsMessagesListener);
        }

        if (systemMessagesManager != null) {
            systemMessagesManager.removeSystemMessageListener(systemMessagesListener);
        }
        dialogsManager.removeManagingDialogsCallbackListener(this);
    }

    @Override
    public void onDialogCreated(QBChatDialog chatDialog) {
    }

    @Override
    public void onDialogUpdated(String chatDialog) {

    }

    @Override
    public void onNewDialogLoaded(QBChatDialog chatDialog) {

    }

    private class SystemMessagesListener implements QBSystemMessageListener {
        @Override
        public void processMessage(final QBChatMessage qbChatMessage) {
            dialogsManager.onSystemMessageReceived(qbChatMessage);
        }

        @Override
        public void processError(QBChatException e, QBChatMessage qbChatMessage) {

        }
    }

    private BroadcastReceiver mNotificationReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
        }
    };


}

这是我的 Fragment viewModel 代码:-

public class ConversationViewModel extends Observable implements DialogsManager.ManagingDialogsCallbacks {
    private static final int REQUEST_DIALOG_ID_FOR_UPDATE = 165;
    public Context mContext;
    FragmentConversationBinding binding;
    private ActionMode currentActionMode;
    public QBRequestGetBuilder requestBuilder;
    private int skipRecords = 0;
    private DialogsAdapter dialogsAdapter;
    private QBChatDialogMessageListener allDialogsMessagesListener;
    private SystemMessagesListener systemMessagesListener;
    private QBSystemMessagesManager systemMessagesManager;
    private QBIncomingMessagesManager incomingMessagesManager;
    private DialogsManager dialogsManager;
    private QBUser currentUser;
    private String id;

    public ConversationViewModel(Context mContext, FragmentConversationBinding binding, String Dialogid) {
        this.mContext = mContext;
        this.binding = binding;
        this.id = Dialogid;
        initUI();
    }

    public void initUI() {
        allDialogsMessagesListener = new AllDialogsMessageListener();
        systemMessagesListener = new SystemMessagesListener();
        dialogsManager = new DialogsManager();
        currentUser = ChatHelper.getCurrentUser();
        initList();
        registerQbChatListeners();
        if (QbDialogHolder.getInstance().getDialogs().size() > 0) {
            loadDialogsFromQb(true, true);
        } else {
            loadDialogsFromQb(false, true);
        }
        if (!id.isEmpty()) {
            loadUpdatedDialog(id);
            id = "";
        } else {
            updateDialogsList();
        }
    }

    public void initList() {
        dialogsAdapter = new DialogsAdapter(mContext, new ArrayList<>(QbDialogHolder.getInstance().getDialogs().values()));
        binding.listDialogsChats.setAdapter(dialogsAdapter);
        binding.listDialogsChats.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                QBChatDialog selectedDialog = (QBChatDialog) parent.getItemAtPosition(position);
                if (currentActionMode == null) {
                    ChatActivity.startForResult(((Activity) mContext), REQUEST_DIALOG_ID_FOR_UPDATE, selectedDialog);
                } else {
                    dialogsAdapter.toggleSelection(selectedDialog);
                }
            }
        });
        binding.listDialogsChats.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
            @Override
            public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
                QBChatDialog selectedDialog = (QBChatDialog) parent.getItemAtPosition(position);
                dialogsAdapter.selectItem(selectedDialog);
                return true;
            }
        });
        requestBuilder = new QBRequestGetBuilder();
        binding.swipyRefreshLayout.setOnRefreshListener(new SwipyRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh(SwipyRefreshLayoutDirection direction) {
                requestBuilder.setSkip(skipRecords += ChatHelper.DIALOG_ITEMS_PER_PAGE);
                loadDialogsFromQb(true, false);
            }
        });


    }

    private void loadUpdatedDialog(String dialogId) {
        ChatHelper.getInstance().getDialogById(dialogId, new QbEntityCallbackImpl<QBChatDialog>() {
            @Override
            public void onSuccess(QBChatDialog result, Bundle bundle) {
                QbDialogHolder.getInstance().addDialog(result);
                updateDialogsAdapter();
            }

            @Override
            public void onError(QBResponseException e) {

            }
        });
    }

    public void updateDialogsList() {
        requestBuilder.setSkip(skipRecords = 0);
        loadDialogsFromQb(true, true);
    }

    @Override
    public void onDialogCreated(QBChatDialog chatDialog) {
        updateDialogsAdapter();
    }

    @Override
    public void onDialogUpdated(String chatDialog) {
        updateDialogsAdapter();
    }

    @Override
    public void onNewDialogLoaded(QBChatDialog chatDialog) {
        updateDialogsAdapter();
    }


    private void registerQbChatListeners() {
        incomingMessagesManager = QBChatService.getInstance().getIncomingMessagesManager();
        systemMessagesManager = QBChatService.getInstance().getSystemMessagesManager();

        if (incomingMessagesManager != null) {
            incomingMessagesManager.addDialogMessageListener(allDialogsMessagesListener != null
                    ? allDialogsMessagesListener : new AllDialogsMessageListener());
        }

        if (systemMessagesManager != null) {
            systemMessagesManager.addSystemMessageListener(systemMessagesListener != null
                    ? systemMessagesListener : new SystemMessagesListener());
        }

        dialogsManager.addManagingDialogsCallbackListener(this);
    }


    private class SystemMessagesListener implements QBSystemMessageListener {
        @Override
        public void processMessage(final QBChatMessage qbChatMessage) {
            dialogsManager.onSystemMessageReceived(qbChatMessage);
        }

        @Override
        public void processError(QBChatException e, QBChatMessage qbChatMessage) {

        }
    }

    private class AllDialogsMessageListener extends QbChatDialogMessageListenerImp {
        @Override
        public void processMessage(final String dialogId, final QBChatMessage qbChatMessage, Integer senderId) {
            if (!senderId.equals(ChatHelper.getCurrentUser().getId())) {
                dialogsManager.onGlobalMessageReceived(dialogId, qbChatMessage);
            }
        }
    }

    public void updateDialogsAdapter() {
        dialogsAdapter.updateList(new ArrayList<>(QbDialogHolder.getInstance().getDialogs().values()));
    }

    public void loadDialogsFromQb(final boolean silentUpdate, final boolean clearDialogHolder) {
        if (!silentUpdate) {
            binding.progressDialogs.setVisibility(View.VISIBLE);
        }

        ChatHelper.getInstance().getDialogs(requestBuilder, new QBEntityCallback<ArrayList<QBChatDialog>>() {
            @Override
            public void onSuccess(ArrayList<QBChatDialog> dialogs, Bundle bundle) {
                binding.progressDialogs.setVisibility(View.GONE);
                binding.swipyRefreshLayout.setRefreshing(false);
                if (clearDialogHolder) {
                    QbDialogHolder.getInstance().clear();
                }
                QbDialogHolder.getInstance().addDialogs(dialogs);
                updateDialogsAdapter();
            }

            @Override
            public void onError(QBResponseException e) {
                binding.progressDialogs.setVisibility(View.GONE);
                binding.swipyRefreshLayout.setRefreshing(false);
                Toast.makeText(mContext, e.getMessage(), Toast.LENGTH_SHORT).show();
            }
        });
    }
}

这是我的聊天 Activity :-

public class ChatActivity extends BindingActivity<ActivityChatBinding> implements OnImagePickedListener {
    ChatViewModel chatViewModel;
    public static final int REQUEST_CODE_ATTACHMENT = 721;
    public static final String EXTRA_DIALOG_ID = "dialogId";

    @Override
    protected int getLayoutId() {
        return R.layout.activity_chat;
    }

    public static void startForResult(Activity activity, int code, QBChatDialog dialogId) {
        Intent intent = new Intent(activity, ChatActivity.class);
        intent.putExtra(ChatActivity.EXTRA_DIALOG_ID, dialogId);
        activity.startActivityForResult(intent, code);
    }

    @Override
    public void setInitBinding() {
        getWindow().setBackgroundDrawableResource(R.drawable.bg_img);
        chatViewModel = new ChatViewModel(this, binding);
        binding.setChatViewModel(chatViewModel);
    }

    @Override
    protected void setUpObserver() {
        chatViewModel.addObserver(this);
    }

    @Override
    public void update(Observable o, Object arg) {
    }

    @Override
    public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
        super.onSaveInstanceState(outState, outPersistentState);
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
    }

    @Override
    public void onBackPressed() {
        binding.getChatViewModel().releaseChat();
        binding.getChatViewModel().sendDialogId();
        super.onBackPressed();
    }

    @Override
    public void onImagePicked(int requestCode, File file) {
        switch (requestCode) {
            case REQUEST_CODE_ATTACHMENT:
                binding.getChatViewModel().attachmentPreviewAdapter.add(file);
                break;
        }
    }

    @Override
    public void onImagePickError(int requestCode, Exception e) {
        Toast.makeText(this, e.toString(), Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onImagePickClosed(int requestCode) {

    }

}

聊天 Activity View 模型 :-

public class ChatViewModel extends Observable {
    public Context mContext;
    ActivityChatBinding binding;
    public static final String TAG = ChatActivity.class.getSimpleName();
    public static final int REQUEST_CODE_ATTACHMENT = 721;
    public static final String PROPERTY_SAVE_TO_HISTORY = "save_to_history";
    public static final String EXTRA_DIALOG_ID = "dialogId";
    public ChatAdapter chatAdapter;
    public AttachmentPreviewAdapter attachmentPreviewAdapter;
    public QBChatDialog qbChatDialog;
    public ArrayList<QBChatMessage> unShownMessages;
    public int skipPagination = 0;
    public ChatMessageListener chatMessageListener;
    QBPrivacyList qbPrivacyList;
    public static boolean mBlock;
    public static int userId, recipientId;
    public QBPrivacyListsManager privacyListsManager;
    public ObservableField<String> chatMessage = new ObservableField<>("");

    public ChatViewModel(Context mContext, ActivityChatBinding binding) {
        this.mContext = mContext;
        this.binding = binding;
        Log.v(TAG, "onCreate ChaActivity on Thread ID = " + Thread.currentThread().getId());
        qbChatDialog = (QBChatDialog) ((Activity) mContext).getIntent().getSerializableExtra(EXTRA_DIALOG_ID);

        Log.v(TAG, "deserialized dialog = " + qbChatDialog);
        qbChatDialog.initForChat(QBChatService.getInstance());
        chatMessageListener = new ChatMessageListener();
        qbPrivacyList = new QBPrivacyList();
        privacyListsManager = QBChatService.getInstance().getPrivacyListsManager();
        qbChatDialog.addMessageListener(chatMessageListener);
        initViews();
        initChat();
    }


    public void initViews() {
        try {
            recipientId = qbChatDialog.getRecipientId();
            userId = qbChatDialog.getUserId();
            getPrivacyList();
        } catch (SmackException.NotConnectedException e) {
            e.printStackTrace();
        } catch (XMPPException.XMPPErrorException e) {
            e.printStackTrace();
        } catch (SmackException.NoResponseException e) {
            e.printStackTrace();
        }
        attachmentPreviewAdapter = new AttachmentPreviewAdapter(mContext,
                new AttachmentPreviewAdapter.OnAttachmentCountChangedListener() {
                    @Override
                    public void onAttachmentCountChanged(int count) {
                        binding.attachmentContainer.setVisibility(count == 0 ? View.GONE : View.VISIBLE);
                    }
                },
                new AttachmentPreviewAdapter.OnAttachmentUploadErrorListener() {
                    @Override
                    public void onAttachmentUploadError(QBResponseException e) {
                        Toast.makeText(mContext, e.toString(), Toast.LENGTH_SHORT).show();
                    }
                });
        binding.attachmentAdapter.setAdapter(attachmentPreviewAdapter);


    }

    public void onAttachmentsClick(View view) {
        new ImagePickHelper().pickAnImage(((FragmentActivity) mContext), REQUEST_CODE_ATTACHMENT);
    }

    private void initChat() {
        switch (qbChatDialog.getType()) {
            case PRIVATE:
                loadDialogUsers();
                break;

            default:
                Toaster.shortToast(String.format("%s %s", getString(R.string.chat_unsupported_type), qbChatDialog.getType().name()));
                ((Activity) mContext).finish();
                break;
        }
    }


    public void loadDialogUsers() {
        ChatHelper.getInstance().getUsersFromDialog(qbChatDialog, new QBEntityCallback<ArrayList<QBUser>>() {
            @Override
            public void onSuccess(ArrayList<QBUser> users, Bundle bundle) {
                String chatName = QbDialogUtils.getDialogName(qbChatDialog);
                binding.dialogName.setText(chatName);
                loadChatHistory();
            }

            @Override
            public void onError(QBResponseException e) {
                Toast.makeText(mContext, e.toString(), Toast.LENGTH_SHORT).show();
            }
        });
    }

    public void loadChatHistory() {
        ChatHelper.getInstance().loadChatHistory(qbChatDialog, skipPagination, new QBEntityCallback<ArrayList<QBChatMessage>>() {
            @Override
            public void onSuccess(ArrayList<QBChatMessage> messages, Bundle args) {
                Collections.reverse(messages);
                if (chatAdapter == null) {
                    chatAdapter = new ChatAdapter(mContext, qbChatDialog, messages);
                    chatAdapter.setPaginationHistoryListener(new PaginationHistoryListener() {
                        @Override
                        public void downloadMore() {
                            loadChatHistory();
                        }
                    });
                    chatAdapter.setOnItemInfoExpandedListener(new ChatAdapter.OnItemInfoExpandedListener() {
                        @Override
                        public void onItemInfoExpanded(final int position) {
                            if (isLastItem(position)) {
                                ((Activity) mContext).runOnUiThread(new Runnable() {
                                    @Override
                                    public void run() {
                                        binding.listChat.setSelection(position);
                                    }
                                });
                            } else {
                                binding.listChat.smoothScrollToPosition(position);
                            }
                        }

                        private boolean isLastItem(int position) {
                            return position == chatAdapter.getCount() - 1;
                        }
                    });
                    if (unShownMessages != null && !unShownMessages.isEmpty()) {
                        List<QBChatMessage> chatList = chatAdapter.getList();
                        for (QBChatMessage message : unShownMessages) {
                            if (!chatList.contains(message)) {
                                chatAdapter.add(message);
                            }
                        }
                    }
                    binding.listChat.setAdapter(chatAdapter);
                    binding.listChat.setAreHeadersSticky(false);
                    binding.listChat.setDivider(null);
                } else {
                    chatAdapter.addList(messages);
                    binding.listChat.setSelection(messages.size());
                }
                binding.progressBar.setVisibility(View.GONE);
            }

            @Override
            public void onError(QBResponseException e) {
                binding.progressBar.setVisibility(View.GONE);
                skipPagination -= ChatHelper.CHAT_HISTORY_ITEMS_PER_PAGE;
                Toast.makeText(mContext, e.toString(), Toast.LENGTH_SHORT).show();
            }
        });
        skipPagination += ChatHelper.CHAT_HISTORY_ITEMS_PER_PAGE;

        QBRestChatService.markMessagesAsRead(qbChatDialog.getDialogId(), null);

    }


    public class ChatMessageListener extends QbChatDialogMessageListenerImp {
        @Override
        public void processMessage(String s, QBChatMessage qbChatMessage, Integer integer) {
            showMessage(qbChatMessage);
        }
    }

    public void showMessage(QBChatMessage message) {
        if (chatAdapter != null) {
            chatAdapter.add(message);
            scrollMessageListDown();
        } else {
            if (unShownMessages == null) {
                unShownMessages = new ArrayList<>();
            }
            unShownMessages.add(message);
        }
    }


    public void scrollMessageListDown() {
        binding.listChat.setSelection(binding.listChat.getCount() - 1);
    }

    public void onSendChatClick(View view) {
        int totalAttachmentsCount = attachmentPreviewAdapter.getCount();
        Collection<QBAttachment> uploadedAttachments = attachmentPreviewAdapter.getUploadedAttachments();
        if (!uploadedAttachments.isEmpty()) {
            if (uploadedAttachments.size() == totalAttachmentsCount) {
                for (QBAttachment attachment : uploadedAttachments) {
                    sendChatMessage(null, attachment);
                }
            } else {
                Toaster.shortToast(R.string.chat_wait_for_attachments_to_upload);
            }
        }

        String text = binding.getChatViewModel().chatMessage.get().trim();
        if (!TextUtils.isEmpty(text)) {
            sendChatMessage(text, null);
        }
    }

    public void sendChatMessage(String text, QBAttachment attachment) {
        QBChatMessage chatMessage = new QBChatMessage();
        if (attachment != null) {
            chatMessage.addAttachment(attachment);
        } else {
            chatMessage.setBody(text);
        }
        chatMessage.setProperty(PROPERTY_SAVE_TO_HISTORY, "1");
        chatMessage.setDateSent(System.currentTimeMillis() / 1000);
        chatMessage.setMarkable(true);

        if (!QBDialogType.PRIVATE.equals(qbChatDialog.getType()) && !qbChatDialog.isJoined()) {
            Toaster.shortToast("You're still joining a group chat, please wait a bit");
            return;
        }

        try {
            qbChatDialog.sendMessage(chatMessage);

            if (QBDialogType.PRIVATE.equals(qbChatDialog.getType())) {
                showMessage(chatMessage);
            }

            if (attachment != null) {
                attachmentPreviewAdapter.remove(attachment);
            } else {
                binding.getChatViewModel().chatMessage.set("");
            }
        } catch (SmackException.NotConnectedException e) {
            Log.w(TAG, e);
            Toaster.shortToast("Can't send a message, You are not connected to chat");
        }
    }

    public void leaveGroupDialog() {
        try {
            ChatHelper.getInstance().leaveChatDialog(qbChatDialog);
        } catch (XMPPException | SmackException.NotConnectedException e) {
            Log.w(TAG, e);
        }
    }

    public void releaseChat() {
        qbChatDialog.removeMessageListrener(chatMessageListener);
        if (!QBDialogType.PRIVATE.equals(qbChatDialog.getType())) {
            leaveGroupDialog();
        }
    }

    public void sendDialogId() {
        Intent result = new Intent();
        result.putExtra(EXTRA_DIALOG_ID, qbChatDialog.getDialogId());
        ((Activity) mContext).setResult(RESULT_OK, result);
    }

    public void finishActivity(View view) {
        ((Activity) mContext).finish();
    }

    public void clickButtonBlock(View view) {
        AppUtils.dialog(mContext);
        if (mBlock) {
            onClickUnblock();
        } else {
            onClickBlock();
        }
    }

    public void getPrivacyList() throws SmackException.NotConnectedException, XMPPException.XMPPErrorException, SmackException.NoResponseException {
        QBPrivacyListsManager privacyListsManager = QBChatService.getInstance().getPrivacyListsManager();
        QBPrivacyList privacyList = privacyListsManager.getPrivacyList("public");
        List<QBPrivacyListItem> items = privacyList.getItems();
        int i;
        for (i = 0; i < privacyList.getItems().size(); i++) {
            QBPrivacyListItem item = items.get(i);
            String valueForType = item.getValueForType();
            String[] splitvalueType = valueForType.split("-");
            String blockId = splitvalueType[0];
            if (blockId.equalsIgnoreCase(String.valueOf(recipientId))) {
                mBlock = true;
                binding.tvBlock.setText("Unblock");
                break;
            } else {
                binding.tvBlock.setText("Block");
            }
        }
    }

}

问题是当我进入聊天室并返回到我的聊天列表页面时,chatDialog.getUnreadMessageCount() 给我计数 2。

问题描述:-

例子:- 我已经在两台设备上安装了我的应用程序。当我从一个(设备 A)向另一个(设备 B)发送消息时。设备 B 将显示正确的未读计数,即 1 。现在,当我单击聊天对话框并进入(设备 B 的)聊天室时。然后返回到它的列表页面,然后再次尝试从设备 A 向设备 B 发送消息。这次未读计数为 2,但它应该是一个,因为我已经查看了我之前的消息。如果我尝试再次打开我的聊天室以阅读消息(设备 B)并在阅读消息后返回我的列表页面(设备 B),情况会变得更糟。这一次,如果我从设备 A 向设备 B 发送消息,那么我的一条消息的计数结果为“5”,这让我感到恶心,我调试了整个代码,它是 chatDialog .getUnreadMessageCount() 回复我的人数“5”,我不知道为什么我以前的消息不计为已读消息以及为什么每次我打开聊天室都会在未读计数中添加一个额外的数字。请帮助我出来了,这两天我都在摸不着头脑。

非常感谢您的帮助。

谢谢

最佳答案

问题是您的 AllDialogsMessageListener 没有被注销,所以通过调用 unregisterQbChatListeners() 来注销它;当您打开聊天室屏幕时。一旦您这样做,一切都会正常进行。

这就是我的意思:-

binding.listDialogsChats.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                QBChatDialog selectedDialog = (QBChatDialog) parent.getItemAtPosition(position);
                if (currentActionMode == null) {
                    unregisterQbChatListeners();
                    ChatActivity.startForResult(((Activity) mContext), REQUEST_DIALOG_ID_FOR_UPDATE, selectedDialog);
                } else {
                    dialogsAdapter.toggleSelection(selectedDialog);
                }
            }
        });

private void unregisterQbChatListeners() {
        if (incomingMessagesManager != null) {
            incomingMessagesManager.removeDialogMessageListrener(allDialogsMessagesListener);
        }

        if (systemMessagesManager != null) {
            systemMessagesManager.removeSystemMessageListener(systemMessagesListener);
        }

        dialogsManager.removeManagingDialogsCallbackListener(this);
    }

干杯!!!

关于android - 依靠聊天无法正常工作 QuickBlox,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46363242/

有关android - 依靠聊天无法正常工作 QuickBlox的更多相关文章

  1. ruby-on-rails - 由于 "wkhtmltopdf",PDFKIT 显然无法正常工作 - 2

    我在从html页面生成PDF时遇到问题。我正在使用PDFkit。在安装它的过程中,我注意到我需要wkhtmltopdf。所以我也安装了它。我做了PDFkit的文档所说的一切......现在我在尝试加载PDF时遇到了这个错误。这里是错误:commandfailed:"/usr/local/bin/wkhtmltopdf""--margin-right""0.75in""--page-size""Letter""--margin-top""0.75in""--margin-bottom""0.75in""--encoding""UTF-8""--margin-left""0.75in""-

  2. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  3. ruby-on-rails - 无法使用 Rails 3.2 创建插件? - 2

    我对最新版本的Rails有疑问。我创建了一个新应用程序(railsnewMyProject),但我没有脚本/生成,只有脚本/rails,当我输入ruby./script/railsgeneratepluginmy_plugin"Couldnotfindgeneratorplugin.".你知道如何生成插件模板吗?没有这个命令可以创建插件吗?PS:我正在使用Rails3.2.1和ruby​​1.8.7[universal-darwin11.0] 最佳答案 随着Rails3.2.0的发布,插件生成器已经被移除。查看变更日志here.现在

  4. ruby - 无法运行 Rails 2.x 应用程序 - 2

    我尝试运行2.x应用程序。我使用rvm并为此应用程序设置其他版本的ruby​​:$rvmuseree-1.8.7-head我尝试运行服务器,然后出现很多错误:$script/serverNOTE:Gem.source_indexisdeprecated,useSpecification.Itwillberemovedonorafter2011-11-01.Gem.source_indexcalledfrom/Users/serg/rails_projects_terminal/work_proj/spohelp/config/../vendor/rails/railties/lib/r

  5. ruby-on-rails - 无法在centos上安装therubyracer(V8和GCC出错) - 2

    我正在尝试在我的centos服务器上安装therubyracer,但遇到了麻烦。$geminstalltherubyracerBuildingnativeextensions.Thiscouldtakeawhile...ERROR:Errorinstallingtherubyracer:ERROR:Failedtobuildgemnativeextension./usr/local/rvm/rubies/ruby-1.9.3-p125/bin/rubyextconf.rbcheckingformain()in-lpthread...yescheckingforv8.h...no***e

  6. ruby - 无法让 RSpec 工作—— 'require' : cannot load such file - 2

    我花了三天的时间用头撞墙,试图弄清楚为什么简单的“rake”不能通过我的规范文件。如果您遇到这种情况:任何文件夹路径中都不要有空格!。严重地。事实上,从现在开始,您命名的任何内容都没有空格。这是我的控制台输出:(在/Users/*****/Desktop/LearningRuby/learn_ruby)$rake/Users/*******/Desktop/LearningRuby/learn_ruby/00_hello/hello_spec.rb:116:in`require':cannotloadsuchfile--hello(LoadError) 最佳

  7. ruby-on-rails - rspec should have_select ('cars' , :options => ['volvo' , 'saab' ] 不工作 - 2

    关闭。这个问题需要detailsorclarity.它目前不接受答案。想改进这个问题吗?通过editingthispost添加细节并澄清问题.关闭8年前。Improvethisquestion在首页我有:汽车:VolvoSaabMercedesAudistatic_pages_spec.rb中的测试代码:it"shouldhavetherightselect"dovisithome_pathit{shouldhave_select('cars',:options=>['volvo','saab','mercedes','audi'])}end响应是rspec./spec/request

  8. ruby-on-rails - s3_direct_upload 在生产服务器中不工作 - 2

    在Rails4.0.2中,我使用s3_direct_upload和aws-sdkgems直接为s3存储桶上传文件。在开发环境中它工作正常,但在生产环境中它会抛出如下错误,ActionView::Template::Error(noimplicitconversionofnilintoString)在View中,create_cv_url,:id=>"s3_uploader",:key=>"cv_uploads/{unique_id}/${filename}",:key_starts_with=>"cv_uploads/",:callback_param=>"cv[direct_uplo

  9. ruby - 无法覆盖 irb 中的 to_s - 2

    我在pry中定义了一个函数:to_s,但我无法调用它。这个方法去哪里了,怎么调用?pry(main)>defto_spry(main)*'hello'pry(main)*endpry(main)>to_s=>"main"我的ruby版本是2.1.2看了一些答案和搜索后,我认为我得到了正确的答案:这个方法用在什么地方?在irb或pry中定义方法时,会转到Object.instance_methods[1]pry(main)>defto_s[1]pry(main)*'hello'[1]pry(main)*end=>:to_s[2]pry(main)>defhello[2]pry(main)

  10. ruby - 无法在 60 秒内获得稳定的 Firefox 连接 (127.0.0.1 :7055) - 2

    我使用的是Firefox版本36.0.1和Selenium-Webdrivergem版本2.45.0。我能够创建Firefox实例,但无法使用脚本继续进行进一步的操作无法在60秒内获得稳定的Firefox连接(127.0.0.1:7055)错误。有人能帮帮我吗? 最佳答案 我遇到了同样的问题。降级到firefoxv33后一切正常。您可以找到旧版本here 关于ruby-无法在60秒内获得稳定的Firefox连接(127.0.0.1:7055),我们在StackOverflow上找到一个类

随机推荐